diff options
Diffstat (limited to 'private/windows/diamond')
157 files changed, 46243 insertions, 0 deletions
diff --git a/private/windows/diamond/asrt.c b/private/windows/diamond/asrt.c new file mode 100644 index 000000000..3a1966786 --- /dev/null +++ b/private/windows/diamond/asrt.c @@ -0,0 +1,182 @@ +/*** asrt.c - Assertion Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Lift code from 1988 PSCHAR.EXE + * 12-Aug-1993 bens Improve documentation, move messages to asrt.msg + * 14-Aug-1993 bens Add assertion flags, query calls + * + * Functions available in ASSERT build: + * AssertRegisterFunc - Register assertion failure call back function + * AsrtCheck - Check that parameter is TRUE + * AsrtStruct - Check that pointer points to specified structure + * AssertForce - Force an assertion failure + */ + +#include "types.h" +#include "asrt.h" + +#ifdef ASSERT // Must be after asrt.h! + +#include "asrt.msg" + + +void doFailure(char *pszMsg, char *pszFile, int iLine); + +STATIC PFNASSERTFAILURE pfnafClient=NULL; // Assertion call back function +STATIC ASSERTFLAGS asfClient=asfNONE; // Assertion flags + + +/*** AssertRegisterFunc - Register assertion failure call back function + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertRegisterFunc(PFNASSERTFAILURE pfnaf) +{ + pfnafClient = pfnaf; // Store for future use +} + + +/*** AssertGetFunc - Get current assertion failure call back function + * + * NOTE: See asrt.h for entry/exit conditions. + */ +PFNASSERTFAILURE AssertGetFunc(void) +{ + return pfnafClient; +} + + +/*** AssertSetFlags - Set special assertion control flags + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertSetFlags(ASSERTFLAGS asf) +{ + asfClient = asf; +} + + +/*** AssertGetFlags - Get special assertion control flags + * + * NOTE: See asrt.h for entry/exit conditions. + */ +ASSERTFLAGS AssertGetFlags(void) +{ + return asfClient; +} + + +/*** AsrtCheck - Check assertion that argument is TRUE + * + * Entry: + * f - Boolean value to check + * pszFile - name of source file + * iLine - source line number + * + * Exit-Success: + * Returns; f was TRUE + * + * Exit-Failure: + * Calls assertion failure callback function; f was false. + */ +void AsrtCheck(BOOL f, char *pszFile, int iLine) +{ + if (!f) { + doFailure(pszASRTERR_FALSE,pszFile,iLine); // Inform client + // Client returned, ignore error! + } +} + + +/*** AsrtStruct - Check assertion that pointer is of correct type + * + * Entry: + * pv - Pointer to structure + * sig - Expected signature + * pszFile - name of source file + * iLine - source line number + * + * Exit-Success: + * Returns; pv != NULL, and pv->sig == sig. + * + * Exit-Failure: + * Calls assertion failure callback function; pv was bad. + */ +void AsrtStruct(void *pv, SIGNATURE sig, char *pszFile, int iLine) +{ + if (pv == NULL) { + doFailure(pszASRTERR_NULL_POINTER,pszFile,iLine); // Inform client + // Client returned, ignore error! + } + else if (*((PSIGNATURE)pv) != sig) { + (*pfnafClient)(pszASRTERR_SIGNATURE_BAD,pszFile,iLine);// Inform client + // Client returned, ignore error! + } +} + + +/*** AssertForce - Force an assertion failure + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertForce(char *pszMsg, char *pszFile, int iLine) +{ + doFailure(pszMsg,pszFile,iLine); // Inform client + // Client returned, ignore error! +} + + +/*** AssertErrPath - Report an internal error path + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertErrPath(char *pszMsg, char *pszFile, int iLine) +{ + //** Only assert if we are not skipping error path assertions + if (!(asfClient & asfSKIP_ERROR_PATH_ASSERTS)) { + doFailure(pszMsg,pszFile,iLine); // Inform client + } + // Client returned, ignore error! +} + + +/*** doFailure - Call registered call back function + * + * Entry: + * pszMsg - Message to display + * pszFile - Name of source file + * iLine - Source line number + * + * Exit-Success: + * Returns; client wanted to ignore assertion. + * + * Exit-Failure: + * Does not return. + */ +void doFailure(char *pszMsg, char *pszFile, int iLine) +{ + if (pfnafClient == NULL) { + //** Call back not registered! + // + // We don't have any output mechanism of our own, since we + // are platform-independent. So, just spin in a loop and + // hope the developer can break in with a debugger to see + // what is wrong! + + for (;;) + ; + } + else { //** Call back registered + (*pfnafClient)(pszMsg,pszFile,iLine); // Inform client + } +} + +#endif // !ASSERT diff --git a/private/windows/diamond/asrt.h b/private/windows/diamond/asrt.h new file mode 100644 index 000000000..b9f56c6c2 --- /dev/null +++ b/private/windows/diamond/asrt.h @@ -0,0 +1,304 @@ +/*** asrt.h - Definitions for Assertion Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Lifted old code from 1988 PSCHAR.EXE + * 14-Aug-1993 bens Added Get/Set functions + * 01-Sep-1993 bens Added AssertSub function + * 10-Feb-1994 bens Added Set/ClearAssertSignature + * 15-Mar-1994 bens Put back AssertMessage macro + * + * Notes: + * o Every data structure must have a signature as first member. + * o Signatures MUST be unique over all structures. + * o sigBAD is a reserved signature. + * o When freeing a structure, blast the signature field with sigBAD. + * o Put an AssertXXX prior to dereferencing any pointer. + * o Signatures in structures and private Assert definitions should only + * be generated if ASSERT is defined. + * + * Functions available in ASSERT build: + * AssertRegisterFunc - Register assertion failure call back function + * AssertGetFunc - Get registered call back function + * + * AssertSetFlags - Set Assertion Manager flags + * AssertGetFlags - Get Assertion Manager flags + * + * Assert - Check that parameter is TRUE + * AssertSub - Check that parameter is TRUE, take explicit filename & line number + * AssertStrucure - Check that pointer points to specified structure + * AssertForce - Force an assertion failure + * AssertErrPath - Error Path assertion failure + * + * SetAssertSignature - Set the signature for a structure + * ClearAssertSignature - Clear the signature for a structure + * + * Other definitions available in ASSERT build: + * PFNASSERTFAILURE - Assertion failure call back function type + * FNASSERTFAILURE - Macro to simplify declaration of call back function + * SIGNATURE - Structure signature type + */ + +#ifndef INCLUDED_ASSERT +#define INCLUDED_ASSERT 1 + +#ifdef _DEBUG +#ifndef ASSERT +#define ASSERT 1 +#endif // !ASSERT +#endif // _DEBUG + +#ifdef ASSERT + +typedef unsigned long ASSERTFLAGS; /* asf - Assertion Manager Flags */ +#define asfNONE 0x00 +#define asfSKIP_ERROR_PATH_ASSERTS 0x01 /* Some clients may wish to set + * assertions in error paths, to + * ensure that the problem is + * noticed in a debug build. But, + * in order to support automated + * testing of error paths, these + * assertions must be disabled. + * This flag allows a test program + * to disable these informational + * asserts! + */ + +typedef unsigned long SIGNATURE; /* sig - structure signature */ +typedef SIGNATURE *PSIGNATURE; /* psig */ +#define sigBAD 0 // Invalid signature for ALL structs + +/*** MAKESIG - construct a structure signature + * + * Entry: + * ch1,ch2,ch3,ch4 - four characters + * + * Exit: + * returns SIGNATURE + */ +#define MAKESIG(ch1,ch2,ch3,ch4) \ + ( ((SIGNATURE)ch1) + \ + (((SIGNATURE)ch2)<< 8) + \ + (((SIGNATURE)ch3)<<16) + \ + (((SIGNATURE)ch4)<<24) ) + +/*** AssertMessage -- Force an Assertion with supplied message + * + * Entry: + * pszMsg -- message to display + * + * Exit: + * none + */ + +#define AssertMessage(pszMsg) AssertForce(pszMsg,__FILE__,__LINE__) + + +/*** PFNASSERTFAILURE - Assertion Failure call back function + *** FNASSERTFAILURE - Define Assertion Failure call back function + * + * Entry: + * pszMsg - Description of failure + * pszFile - File where assertion failed + * iLine - Line number in file where assertion failed + * + * Exit-Success: + * Returns; ignore failure and continue + * + * Exit-Failure: + * Function does not return, but cleans up and exits program. + */ +typedef void (*PFNASSERTFAILURE)(char *pszMsg, char *pszFile, int iLine); +#define FNASSERTFAILURE(fn) void fn(char *pszMsg, char *pszFile, int iLine) + + +/*** AssertRegisterFunc - Register assertion failure call back function + * + * Entry: + * pfnaf - Call back function + * + * Exit-Success: + * Returns; pfnaf is stored in the Assertion Manager + * + * NOTES: + * (1) This function *must* be called prior to executing an assertion + * checks. If not, and an assertion check fails, then the Assertion + * Manager will sit in a spin loop to catch the developer's attention. + */ +void AssertRegisterFunc(PFNASSERTFAILURE pfnaf); + + +/*** AssertGetFunc - Get current assertion failure call back function + * + * Entry: + * none + * + * Exit-Success: + * Returns current call back function registerd in Assertion Manager. + */ +PFNASSERTFAILURE AssertGetFunc(void); + + +/*** AssertSetFlags - Set special assertion control flags + * + * Entry: + * flags - Set with combination of asfXXXX flags + * + * Exit-Success: + * Returns; Flags are modified in Assertion Manager. + */ +void AssertSetFlags(ASSERTFLAGS asf); + + +/*** AssertGetFlags - Get special assertion control flags + * + * Entry: + * none + * + * Exit-Success: + * Returns current Assertion Manager flags. + */ +ASSERTFLAGS AssertGetFlags(void); + + +/*** Assert - Check assertion that argument is true + * + * Entry: + * b - Boolean value to check + * + * Exit-Success: + * Returns; b was TRUE + * + * Exit-Failure: + * Calls assertion failure callback function; b was FALSE + */ +#define Assert(b) AsrtCheck(b,__FILE__,__LINE__) + + +/*** AssertSub - Check assertion, use passed in filename and line number + * + * Entry: + * b - Boolean value to check + * pszFile - File where assertion occurred + * iLine - Line in file where assertion occurred + * + * Exit-Success: + * Returns; b was TRUE + * + * Exit-Failure: + * Calls assertion failure callback function; b was FALSE + */ +#define AssertSub(b,pszFile,iLine) AsrtCheck(b,pszFile,iLine) + + +/*** AssertStructure - Check assertion that pointer is of correct type + * + * Entry: + * pv - Pointer to structure + * sig - Expected signature + * + * Exit-Success: + * Returns; pv != NULL, and pv->sig == sig. + * + * Exit-Failure: + * Calls assertion failure callback function; pv was bad. + */ +#define AssertStructure(pv,sig) AsrtStruct(pv, sig, __FILE__, __LINE__) + + +/*** AssertForce - Force an assertion failure + * + * Entry: + * pszMsg - Message to display + * pszFile - File where assertion occurred + * iLine - Line in file where assertion occurred + * + * Exit-Success: + * Returns; client wanted to ignore assertion. + * + * Exit-Failure: + * Does not return. + */ +void AssertForce(char *pszMsg, char *pszFile, int iLine); + + +/*** AssertErrorPath - Report an internal error path + * + * Entry: + * pszMsg - Message to display + * pszFile - File where assertion occurred + * iLine - Line in file where assertion occurred + * + * Exit-Success: + * Returns; client wanted to ignore assertion. + * + * Exit-Failure: + * Does not return. + */ +void AssertErrPath(char *pszMsg, char *pszFile, int iLine); + + +/*** SetAssertSignature - Set the signature for a structure + * + * Entry: + * p - Structure with member "sigValue" + * sig - Signature to set + * + * Exit: + * p->sig = sig + */ +#define SetAssertSignature(p,sigValue) p->sig = sigValue + + +/*** ClearAssertSignature - Clear the signature for a structure + * + * Entry: + * p - Structure with member "sig" + * + * Exit: + * p->sig = sigBAD + */ +#define ClearAssertSignature(p) p->sig = sigBAD + + +//** Internal assertion manager worker routines + +void AsrtCheck(BOOL f, char *pszFile, int iLine); +void AsrtStruct(void *pv, SIGNATURE sig, char *pszFile, int iLine); + + +#else // !ASSERT + +//** Assertion checking is turned off, so it all evaporates! + +#define FNASSERTFAILURE(fn) +#define AssertRegisterFunc(pfnaf) +#define Assert(b) +#define AssertSub(b,pszFile,iLine) +#define AssertStructure(pv,sig) +#define AssertMessage(pszMsg) +#define AssertForce(pszMsg,pszFile,iLine) +#define AssertErrPath(pszMsg,pszFile,iLine) +#define SetAssertSignature(p,sig) +#define ClearAssertSignature(p) + +/** The following functions are not defined away, because any valid use + * of them requires a typedef'd variable or function that is not available + * in a non-ASSERT build. So we don't define them so that if a client + * has used these outside of an #ifdef ASSERT, a compiler error/warning + * will be generated: + * + * AssertGetFunc + * AssertSetFlags + * AssertGetFlags + */ + +#endif // ASSERT +#endif // !INCLUDED_ASSERT diff --git a/private/windows/diamond/asrt.msg b/private/windows/diamond/asrt.msg new file mode 100644 index 000000000..17c1a3708 --- /dev/null +++ b/private/windows/diamond/asrt.msg @@ -0,0 +1,22 @@ +/*** asrt.msg - Displayable strings for asrt.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 12-Aug-1993 bens Moved from strings.h + */ + +//** Error Messages + +#ifdef ASSERT + +#define pszASRTERR_FALSE "Condition false" +#define pszASRTERR_NULL_POINTER "Null pointer" +#define pszASRTERR_SIGNATURE_BAD "Signature mismatch" + +#endif // !ASSERT diff --git a/private/windows/diamond/chuck/asrt.c b/private/windows/diamond/chuck/asrt.c new file mode 100644 index 000000000..126063537 --- /dev/null +++ b/private/windows/diamond/chuck/asrt.c @@ -0,0 +1,178 @@ +/*** asrt.c - Assertion Manager + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Lift code from 1988 PSCHAR.EXE + * 12-Aug-1993 bens Improve documentation, move messages to asrt.msg + * 14-Aug-1993 bens Add assertion flags, query calls + * + * Functions available in ASSERT build: + * AssertRegisterFunc - Register assertion failure call back function + * AsrtCheck - Check that parameter is TRUE + * AsrtStruct - Check that pointer points to specified structure + * AssertForce - Force an assertion failure + */ + +#include "types.h" +#include "asrt.h" + +#ifdef ASSERT // Must be after asrt.h! + +#include "asrt.msg" + + +void doFailure(char *pszMsg, char *pszFile, int iLine); + +STATIC PFNASSERTFAILURE pfnafClient=NULL; // Assertion call back function +STATIC ASSERTFLAGS asfClient=asfNONE; // Assertion flags + + +/*** AssertRegisterFunc - Register assertion failure call back function + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertRegisterFunc(PFNASSERTFAILURE pfnaf) +{ + pfnafClient = pfnaf; // Store for future use +} + + +/*** AssertGetFunc - Get current assertion failure call back function + * + * NOTE: See asrt.h for entry/exit conditions. + */ +PFNASSERTFAILURE AssertGetFunc(void) +{ + return pfnafClient; +} + + +/*** AssertSetFlags - Set special assertion control flags + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertSetFlags(ASSERTFLAGS asf) +{ + asfClient = asf; +} + + +/*** AssertGetFlags - Get special assertion control flags + * + * NOTE: See asrt.h for entry/exit conditions. + */ +ASSERTFLAGS AssertGetFlags(void) +{ + return asfClient; +} + + +/*** AsrtCheck - Check assertion that argument is TRUE + * + * Entry: + * f - Boolean value to check + * pszFile - name of source file + * iLine - source line number + * + * Exit-Success: + * Returns; f was TRUE + * + * Exit-Failure: + * Calls assertion failure callback function; f was false. + */ +void AsrtCheck(BOOL f, char *pszFile, int iLine) +{ + if (!f) { + doFailure(pszASRTERR_FALSE,pszFile,iLine); // Inform client + // Client returned, ignore error! + } +} + + +/*** AsrtStruct - Check assertion that pointer is of correct type + * + * Entry: + * pv - Pointer to structure + * sig - Expected signature + * pszFile - name of source file + * iLine - source line number + * + * Exit-Success: + * Returns; pv != NULL, and pv->sig == sig. + * + * Exit-Failure: + * Calls assertion failure callback function; pv was bad. + */ +void AsrtStruct(void *pv, SIGNATURE sig, char *pszFile, int iLine) +{ + if (pv == NULL) { + doFailure(pszASRTERR_NULL_POINTER,pszFile,iLine); // Inform client + // Client returned, ignore error! + } + else if (*((PSIGNATURE)pv) != sig) { + (*pfnafClient)(pszASRTERR_SIGNATURE_BAD,pszFile,iLine);// Inform client + // Client returned, ignore error! + } +} + + +/*** AssertForce - Force an assertion failure + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertForce(char *pszMsg, char *pszFile, int iLine) +{ + doFailure(pszMsg,pszFile,iLine); // Inform client + // Client returned, ignore error! +} + + +/*** AssertErrPath - Report an internal error path + * + * NOTE: See asrt.h for entry/exit conditions. + */ +void AssertErrPath(char *pszMsg, char *pszFile, int iLine) +{ + //** Only assert if we are not skipping error path assertions + if (!(asfClient & asfSKIP_ERROR_PATH_ASSERTS)) { + doFailure(pszMsg,pszFile,iLine); // Inform client + } + // Client returned, ignore error! +} + + +/*** doFailure - Call registered call back function + * + * Entry: + * pszMsg - Message to display + * pszFile - Name of source file + * iLine - Source line number + * + * Exit-Success: + * Returns; client wanted to ignore assertion. + * + * Exit-Failure: + * Does not return. + */ +void doFailure(char *pszMsg, char *pszFile, int iLine) +{ + if (pfnafClient == NULL) { + //** Call back not registered! + // + // We don't have any output mechanism of our own, since we + // are platform-independent. So, just spin in a loop and + // hope the developer can break in with a debugger to see + // what is wrong! + + for (;;) + ; + } + else { //** Call back registered + (*pfnafClient)(pszMsg,pszFile,iLine); // Inform client + } +} + +#endif // !ASSERT diff --git a/private/windows/diamond/chuck/asrt.h b/private/windows/diamond/chuck/asrt.h new file mode 100644 index 000000000..b9f56c6c2 --- /dev/null +++ b/private/windows/diamond/chuck/asrt.h @@ -0,0 +1,304 @@ +/*** asrt.h - Definitions for Assertion Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Lifted old code from 1988 PSCHAR.EXE + * 14-Aug-1993 bens Added Get/Set functions + * 01-Sep-1993 bens Added AssertSub function + * 10-Feb-1994 bens Added Set/ClearAssertSignature + * 15-Mar-1994 bens Put back AssertMessage macro + * + * Notes: + * o Every data structure must have a signature as first member. + * o Signatures MUST be unique over all structures. + * o sigBAD is a reserved signature. + * o When freeing a structure, blast the signature field with sigBAD. + * o Put an AssertXXX prior to dereferencing any pointer. + * o Signatures in structures and private Assert definitions should only + * be generated if ASSERT is defined. + * + * Functions available in ASSERT build: + * AssertRegisterFunc - Register assertion failure call back function + * AssertGetFunc - Get registered call back function + * + * AssertSetFlags - Set Assertion Manager flags + * AssertGetFlags - Get Assertion Manager flags + * + * Assert - Check that parameter is TRUE + * AssertSub - Check that parameter is TRUE, take explicit filename & line number + * AssertStrucure - Check that pointer points to specified structure + * AssertForce - Force an assertion failure + * AssertErrPath - Error Path assertion failure + * + * SetAssertSignature - Set the signature for a structure + * ClearAssertSignature - Clear the signature for a structure + * + * Other definitions available in ASSERT build: + * PFNASSERTFAILURE - Assertion failure call back function type + * FNASSERTFAILURE - Macro to simplify declaration of call back function + * SIGNATURE - Structure signature type + */ + +#ifndef INCLUDED_ASSERT +#define INCLUDED_ASSERT 1 + +#ifdef _DEBUG +#ifndef ASSERT +#define ASSERT 1 +#endif // !ASSERT +#endif // _DEBUG + +#ifdef ASSERT + +typedef unsigned long ASSERTFLAGS; /* asf - Assertion Manager Flags */ +#define asfNONE 0x00 +#define asfSKIP_ERROR_PATH_ASSERTS 0x01 /* Some clients may wish to set + * assertions in error paths, to + * ensure that the problem is + * noticed in a debug build. But, + * in order to support automated + * testing of error paths, these + * assertions must be disabled. + * This flag allows a test program + * to disable these informational + * asserts! + */ + +typedef unsigned long SIGNATURE; /* sig - structure signature */ +typedef SIGNATURE *PSIGNATURE; /* psig */ +#define sigBAD 0 // Invalid signature for ALL structs + +/*** MAKESIG - construct a structure signature + * + * Entry: + * ch1,ch2,ch3,ch4 - four characters + * + * Exit: + * returns SIGNATURE + */ +#define MAKESIG(ch1,ch2,ch3,ch4) \ + ( ((SIGNATURE)ch1) + \ + (((SIGNATURE)ch2)<< 8) + \ + (((SIGNATURE)ch3)<<16) + \ + (((SIGNATURE)ch4)<<24) ) + +/*** AssertMessage -- Force an Assertion with supplied message + * + * Entry: + * pszMsg -- message to display + * + * Exit: + * none + */ + +#define AssertMessage(pszMsg) AssertForce(pszMsg,__FILE__,__LINE__) + + +/*** PFNASSERTFAILURE - Assertion Failure call back function + *** FNASSERTFAILURE - Define Assertion Failure call back function + * + * Entry: + * pszMsg - Description of failure + * pszFile - File where assertion failed + * iLine - Line number in file where assertion failed + * + * Exit-Success: + * Returns; ignore failure and continue + * + * Exit-Failure: + * Function does not return, but cleans up and exits program. + */ +typedef void (*PFNASSERTFAILURE)(char *pszMsg, char *pszFile, int iLine); +#define FNASSERTFAILURE(fn) void fn(char *pszMsg, char *pszFile, int iLine) + + +/*** AssertRegisterFunc - Register assertion failure call back function + * + * Entry: + * pfnaf - Call back function + * + * Exit-Success: + * Returns; pfnaf is stored in the Assertion Manager + * + * NOTES: + * (1) This function *must* be called prior to executing an assertion + * checks. If not, and an assertion check fails, then the Assertion + * Manager will sit in a spin loop to catch the developer's attention. + */ +void AssertRegisterFunc(PFNASSERTFAILURE pfnaf); + + +/*** AssertGetFunc - Get current assertion failure call back function + * + * Entry: + * none + * + * Exit-Success: + * Returns current call back function registerd in Assertion Manager. + */ +PFNASSERTFAILURE AssertGetFunc(void); + + +/*** AssertSetFlags - Set special assertion control flags + * + * Entry: + * flags - Set with combination of asfXXXX flags + * + * Exit-Success: + * Returns; Flags are modified in Assertion Manager. + */ +void AssertSetFlags(ASSERTFLAGS asf); + + +/*** AssertGetFlags - Get special assertion control flags + * + * Entry: + * none + * + * Exit-Success: + * Returns current Assertion Manager flags. + */ +ASSERTFLAGS AssertGetFlags(void); + + +/*** Assert - Check assertion that argument is true + * + * Entry: + * b - Boolean value to check + * + * Exit-Success: + * Returns; b was TRUE + * + * Exit-Failure: + * Calls assertion failure callback function; b was FALSE + */ +#define Assert(b) AsrtCheck(b,__FILE__,__LINE__) + + +/*** AssertSub - Check assertion, use passed in filename and line number + * + * Entry: + * b - Boolean value to check + * pszFile - File where assertion occurred + * iLine - Line in file where assertion occurred + * + * Exit-Success: + * Returns; b was TRUE + * + * Exit-Failure: + * Calls assertion failure callback function; b was FALSE + */ +#define AssertSub(b,pszFile,iLine) AsrtCheck(b,pszFile,iLine) + + +/*** AssertStructure - Check assertion that pointer is of correct type + * + * Entry: + * pv - Pointer to structure + * sig - Expected signature + * + * Exit-Success: + * Returns; pv != NULL, and pv->sig == sig. + * + * Exit-Failure: + * Calls assertion failure callback function; pv was bad. + */ +#define AssertStructure(pv,sig) AsrtStruct(pv, sig, __FILE__, __LINE__) + + +/*** AssertForce - Force an assertion failure + * + * Entry: + * pszMsg - Message to display + * pszFile - File where assertion occurred + * iLine - Line in file where assertion occurred + * + * Exit-Success: + * Returns; client wanted to ignore assertion. + * + * Exit-Failure: + * Does not return. + */ +void AssertForce(char *pszMsg, char *pszFile, int iLine); + + +/*** AssertErrorPath - Report an internal error path + * + * Entry: + * pszMsg - Message to display + * pszFile - File where assertion occurred + * iLine - Line in file where assertion occurred + * + * Exit-Success: + * Returns; client wanted to ignore assertion. + * + * Exit-Failure: + * Does not return. + */ +void AssertErrPath(char *pszMsg, char *pszFile, int iLine); + + +/*** SetAssertSignature - Set the signature for a structure + * + * Entry: + * p - Structure with member "sigValue" + * sig - Signature to set + * + * Exit: + * p->sig = sig + */ +#define SetAssertSignature(p,sigValue) p->sig = sigValue + + +/*** ClearAssertSignature - Clear the signature for a structure + * + * Entry: + * p - Structure with member "sig" + * + * Exit: + * p->sig = sigBAD + */ +#define ClearAssertSignature(p) p->sig = sigBAD + + +//** Internal assertion manager worker routines + +void AsrtCheck(BOOL f, char *pszFile, int iLine); +void AsrtStruct(void *pv, SIGNATURE sig, char *pszFile, int iLine); + + +#else // !ASSERT + +//** Assertion checking is turned off, so it all evaporates! + +#define FNASSERTFAILURE(fn) +#define AssertRegisterFunc(pfnaf) +#define Assert(b) +#define AssertSub(b,pszFile,iLine) +#define AssertStructure(pv,sig) +#define AssertMessage(pszMsg) +#define AssertForce(pszMsg,pszFile,iLine) +#define AssertErrPath(pszMsg,pszFile,iLine) +#define SetAssertSignature(p,sig) +#define ClearAssertSignature(p) + +/** The following functions are not defined away, because any valid use + * of them requires a typedef'd variable or function that is not available + * in a non-ASSERT build. So we don't define them so that if a client + * has used these outside of an #ifdef ASSERT, a compiler error/warning + * will be generated: + * + * AssertGetFunc + * AssertSetFlags + * AssertGetFlags + */ + +#endif // ASSERT +#endif // !INCLUDED_ASSERT diff --git a/private/windows/diamond/chuck/buildcab.c b/private/windows/diamond/chuck/buildcab.c new file mode 100644 index 000000000..ab792701b --- /dev/null +++ b/private/windows/diamond/chuck/buildcab.c @@ -0,0 +1,1078 @@ +/*** buildcab.c - Diamond Cabinet Builder routines + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Chuck Strouss + * + * History: + * 28-Dec-1993 chuckst Initial version + * 09-Mar-1994 bens Add CFHEADER.flags support; RESERVE support + * 15-Mar-1994 bens Finish RESERVE support + * 22-Mar-1994 bens Renames for include file improvements + * 28-Mar-1994 bens Store setID and cabinet number + * 10-Aug-1994 bens Bug fix: If new cabinet is on new disk, + * ignore unused space on previous disk. + */ +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys\stat.h> +#include <io.h> +#include <errno.h> + +#include "types.h" +#include "asrt.h" +#include "checksum.h" +#include "fci_int.h" +#include "cabinet.h" +#include "erf.h" +#include "..\mszip\mci.h" // Get MCI definitions for buildcab.h +#include "buildcab.h" + +// Internal function prototypes + +BOOL filecopy(int hfdst, + int hfsrc, + PFNFCISTATUS pfnProgress, + unsigned long *pcbDone, + unsigned long cbTot, + void *pv, + PERF perf); + +void InitCFHEADER(CFHEADER *pcfheader); + +BOOL ReadCFFILEEntry(PCAB pcab, + PFOL pfol); + +BOOL ReadCFDATAEntry(PCAB pcab,CFDATA *pcfdata,PFOL pfol); + +BOOL ReadPSZ(int fh, char *pb, int cb, PERF perf); + +BOOL WritePsz(int hf, + char *psz, + PERF perf); + + + + +HCAB CreateCab( + char *pszFileName, // name to use for CAB files + PERF perf, // error type return structure + PFNFCIFILEPLACED pfnfiledest, // file placement notification + PFNALLOC pfnalloc, // memory allocation function + PFNFREE pfnfree, // memory free function + PFNFCIGETTEMPFILE pfntemp // temp file name generator + ) +{ + PCAB pcab; + + pcab = (PCAB)(*pfnalloc)(sizeof(CAB)); + if (pcab == NULL) { + ErfSetCodes(perf,FCIERR_ALLOC_FAIL,0); + return NULL; + } + + SetAssertSignature(pcab,sigCAB); + AssertCAB(pcab); // Make sure we've really set sig + + pcab->perf = perf; // Save error structure + pcab->pfnfiledest = pfnfiledest; // save file placement notify function + pcab->pfnalloc = pfnalloc; // Save alloc function in our context + pcab->pfnfree = pfnfree; // Save free function in our context + pcab->pfntemp = pfntemp; // Save temp filename generator + + //** clear the forward/back continuation cabinet info + pcab->achCabinetFirst[0]=0; + pcab->achDiskFirst [0]=0; + pcab->achNxtCabFirst [0]=0; + pcab->achNxtDskFirst [0]=0; + pcab->achCabinetNext [0]=0; + pcab->achDiskNext [0]=0; + + + //** Create cabinet temp files + if (!CrTempFiles (pcab->tf,NUM_CAB_TF,pfntemp,pcab->perf)) + { + ClearAssertSignature(pcab); + pcab->pfnfree (pcab); // free our data + return NULL; // error code already filled in + } + + InitCFHEADER(&pcab->cfheader); + pcab->iFolder = 0; // first folder number we'll write + + return HCABfromPCAB(pcab); +} + + +/*** AddFolderToCabinet - Add a completed folder to a cabinet + * + * We may have to split the folder across multiple cabinets! + * + * Entry: + * pcab - cabinet builder context + * pfol - folder builder context + * fGetNextCab - TRUE => call GetNextCab, even if not overflowing + * GetNextCab - callback: Gets continuation cabinet information + * pfnProgress - callback: progress information + * pv - caller's context for callbacks + * + * Exit-Success: + * Returns TRUE; + * + * Exit-Failure: + * Returns FALSE; pcab->perf filled in + */ +BOOL AddFolderToCabinet(PCAB pcab, + PFOL pfol, + BOOL fGetNextCab, + PFNFCIGETNEXTCABINET GetNextCab, + PFNFCISTATUS pfnProgress, + void *pv) +{ + BOOL fCabinetFull; + + UOFF uoffLast; + long cbSlack; // May go negative when vol is overfull + COFF cbDataLast; + COFF cbDataLastEnd; + COFF cbPredict; + UINT cbPartial; + + unsigned long cbDone; // for status reporting + unsigned long cbTot; // ditto + + TF tf[NUM_FOLDER_TF]; // temp files for folder splitting + + CFDATA *pcfdata=NULL; + CFDATA *pcfdataFirstPart=NULL; + CFFOLDER *pcffolder=NULL; + + AssertCAB(pcab); // verify structure signatures + AssertFOL(pfol); + + //** Assume we'll have no forward continuations for this folder + pcab->achNxtCabFirst [0]=0; + pcab->achNxtDskFirst [0]=0; + + //** Flush the folder + if (!FolderFlush(pfol,pfnProgress,pv)) { + goto error; // error already filled in + } + + uoffLast = 0; // Track emitted uncompressed size + + //** Make sure folder has some data in it +//BUGBUG 01-Apr-1994 bens What if the fGetNextCab is true? +// We need to make sure the GetNextCab code path gets followed, but the +// following code just exits. +// + if (!(pfol->tf[iftfCFFOLDER].cb + pfol->tf[iftfCFDATA].cb)) { + return TRUE; // No data, so do not increment iFolder + } + + //** Allocate buffers for reading/writing CFDATA and CFFOLDER structures. + // Due to the RESERVE fields, these are not fixed sized buffers! + if (!(pcfdata = (*pcab->pfnalloc)(pcab->cbCFDataPlusReserve)) || + !(pcfdataFirstPart = (*pcab->pfnalloc)(pcab->cbCFDataPlusReserve)) || + !(pcffolder = (*pcab->pfnalloc)(pcab->cbCFFolderPlusReserve))) { + goto error; + } + + //** Zero buffers so that reserved section is zero + memset(pcfdata,0,pcab->cbCFDataPlusReserve); + memset(pcfdataFirstPart,0,pcab->cbCFDataPlusReserve); + memset(pcffolder,0,pcab->cbCFFolderPlusReserve); + + //** Keep looping through this function until the pending folder is empty + while (pfol->tf[iftfCFFOLDER].cb + pfol->tf[iftfCFDATA].cb) { + + //** See how much space (if any) we need to trim from the end of the + // folder (and push into a new folder). + + cbPredict = pcab->cbCFHeaderPlusReserve + // Predict total size + pcab->cbCFFolderPlusReserve + // and one more CFFOLDER + pcab->tf[ictfCFDATA].cb + + pcab->tf[ictfCFFILE].cb + + pcab->tf[ictfCFFOLDER].cb + + pfol->tf[iftfCFDATA].cb + + pfol->tf[iftfCFFOLDER].cb; + + //** Account of previous/next disk/cabinet name space + if (pcab->achCabinetFirst[0] != '\0') { + cbPredict += 1+strlen(pcab->achCabinetFirst) + + 1+strlen(pcab->achDiskFirst); + } + if (pcab->achCabinetNext[0] != '\0') { + cbPredict += 1+strlen(pcab->achCabinetNext) + + 1+strlen(pcab->achDiskNext); + } + + //** Estimate total bytes we'll move for status callbacks + cbTot = cbPredict*2; // *2 because we read *and* write + cbDone = 0; // Nothing moved so far + + //** Tell client we have started + if (-1 == pfnProgress(statusFolder,cbDone,cbTot,pv)) { + ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0); + goto error; + } + + //** How much room is left over in the cabinet after this folder? + // NOTE: Can be negative for large folders! + cbSlack = (signed long)pcab->ccab.cb - cbPredict; + + //** Create the folder temp files if we have to split this folder + if (!CrTempFiles(tf,2,pcab->pfntemp,pcab->perf)) { + goto error; // error already filled in + } + + //** Do we have to create a new cabinet? + if (fCabinetFull = ((cbSlack < 0) || fGetNextCab)) { + //** Need to adjust for the new continuation strings + if (pcab->achNxtCabFirst[0] == 0) { + //** This is the first continuation for this folder; + // Remember current disk and cabinet names. + strcpy(pcab->achNxtDskFirst,pcab->ccab.szDisk); + strcpy(pcab->achNxtCabFirst,pcab->ccab.szCab); + } + + //** Subtract out our guess at the next cab/disk name, since + // we're going to get the real size by calling the client to + // get their names. + +//BUGBUG 09-Mar-1994 bens Should we move this down below a bit? +// cbPredict is passed to client to figure out when to switch to a new disk. +// by subtracting here, we are underestimating the total cabinet size by +// a little bit. Also, for the first cabinet, we're not going to have +// any next strings, so again we'll be underestimating. +// + if (pcab->achCabinetNext[0] != '\0') { + cbPredict -= (1+strlen(pcab->achCabinetNext) + + 1+strlen(pcab->achDiskNext)) ; + } + + pcab->ccabNext = pcab->ccab; // copy current ccab to a temp buffer + pcab->ccabNext.iCab++; // increment cabinet number + //** Get next cabinet information + pcab->cbCabinetEstimate = cbPredict; // Remember estimate + if (-1 == GetNextCab (&pcab->ccabNext,cbPredict,pv)) { + ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0); + return FALSE; + } + //** NOTE: Not all fields in pcab->ccabNext are respected! + // In particular, the reserve sizes are only used on + // the initial FCICreate call. + + //** Save the strings for forward cabinet links + strcpy(pcab->achCabinetNext,pcab->ccabNext.szCab); + strcpy(pcab->achDiskNext,pcab->ccabNext.szDisk); + + //** Adjust the predicted size based on changed strings + cbPredict += (1+strlen(pcab->ccabNext.szCab) + + 1+strlen(pcab->ccabNext.szDisk)); + + cbSlack = (signed long)pcab->ccab.cb - cbPredict; + + if (pcab->ccabNext.cb == 0) { // 0 implies MAX + pcab->ccabNext.cb = CB_MAX_DISK; + } + } // endif fCabinetFull + + + //** Now scan our data block file, to see how many blocks we'll have + // to chop of the end, and where to split the last block. We also + // need to know the begin and end address of that last block in + // uncompressed space. + + //** Set the type of compression + pcffolder->typeCompress = pfol->typeCompress; + + pcffolder->cCFData = 0; // Count data blocks as we emit them + pcffolder->coffCabStart = pcab->tf[ictfCFDATA].cb; + + //** For now, the offset field will contain relative offset of the + // first data within the CFDATA field. When the CFDATA temp file + // is copied to the final CAB, each of these fields must have the + // base of the CFDATA added in. + cbDataLastEnd = 0; // keep track of uncompressed data size + + //** Rewind the CFDDATA file for this folder + if (-1L == _lseek(pfol->tf[iftfCFDATA].hf,0L,SEEK_SET)) { + ErfSetCodes(pcab->perf,FCIERR_TEMP_FILE,errno); + goto error; + } + + //** First, we'll handle the CFDATA records. Some of them will go + // into the new cabinet, and if there is an overflow, the rest will + // end up in the replacement temp file. There may be a block split + // between them. Also, we'll calculate uoffLast, which describes + // the address in uncompressed space of the last *complete* CFDATA + // block in this file. This is used to decide which CFFILES will + // have to be duplicated in the continuation cabinet. + + while (ReadCFDATAEntry(pcab,pcfdata,pfol)) { + cbDataLast = cbDataLastEnd; // Track of end of last whole block + cbDataLastEnd += pcfdata->cbData + pcab->cbCFDataPlusReserve; + cbPartial = 0; // Make status callback accurate + + //** Does entire block fit in this cabinet? + if (cbDataLastEnd < (cbSlack + pfol->tf[iftfCFDATA].cb)) { + uoffLast += (UOFF)pcfdata->cbUncomp; + pcffolder->cCFData++; // Keep count of blocks we emit + + //** Checksum must be calculated on two blocks and combined via + // CSUMCompute's seed parameter on the second call. + + pcfdata->csum = + CSUMCompute(&(pcfdata->cbData), + pcab->cbCFDataPlusReserve - sizeof(pcfdata->csum), + CSUMCompute(pfol->pchCompr,pcfdata->cbData,0)); + + if (!WriteCount(&pcab->tf[ictfCFDATA], + pcfdata, + pcab->cbCFDataPlusReserve, + pcab->perf) || + !WriteCount(&pcab->tf[ictfCFDATA], + pfol->pchCompr, + pcfdata->cbData, + pcab->perf)) { + goto error; // error code already filled in + } + } // endif full block that fits + //** Is this our partial block? + else if ((pcab->cbCFDataPlusReserve+cbDataLast) + < (cbSlack + pfol->tf[iftfCFDATA].cb)) { + cbPartial = (UINT)(cbSlack + + pfol->tf[iftfCFDATA].cb - + cbDataLast - + pcab->cbCFDataPlusReserve); + + //** Partial block! Do not update uoffLast! + + pcffolder->cCFData++; // keep count of blocks we emit + //** First partial data blocks have uncomp len == 0 + pcfdataFirstPart->cbUncomp = 0; + pcfdataFirstPart->cbData = cbPartial; // Partial block size + + pcfdata->csum = CSUMCompute(&(pcfdataFirstPart->cbData), + pcab->cbCFDataPlusReserve-sizeof(pcfdataFirstPart->csum), + CSUMCompute(pfol->pchCompr,cbPartial,0)); + + if (!WriteCount(&pcab->tf[ictfCFDATA], + pcfdataFirstPart, + pcab->cbCFDataPlusReserve, + pcab->perf) + || !WriteCount(&pcab->tf[ictfCFDATA], + pfol->pchCompr, + cbPartial, + pcab->perf)) { + goto error; // error already filled in + } + + //** Now save the remainder in our new CFDATA temp file + pcfdata->cbData -= cbPartial; + if (!WriteCount(&tf[iftfCFDATA], + pcfdata, + pcab->cbCFDataPlusReserve, + pfol->perf) + || !WriteCount(&tf[iftfCFDATA], + &pfol->pchCompr[cbPartial], + pcfdata->cbData, + pfol->perf)) { + goto error; // error already filled in + } + } + else { //** this whole block gets pushed into temp file + if (!WriteCount(&tf[iftfCFDATA], + pcfdata, + pcab->cbCFDataPlusReserve, + pfol->perf) + || !WriteCount(&tf[iftfCFDATA], + pfol->pchCompr, + pcfdata->cbData, + pfol->perf)) { + goto error; // error already filled in + } + } + + cbDone += pcfdata->cbData + cbPartial; + + //** Progress callback + if (-1 == pfnProgress(statusFolder,cbDone,cbTot,pv)) { + ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0); + goto error; + } + } // end of while (ReadCFDATA) + + //** Now we've completed the CFFOLDER entry and can write it to its + // temp file + pcab->cfheader.cFolders++; + if (!WriteCount(&pcab->tf[ictfCFFOLDER], + pcffolder, + pcab->cbCFFolderPlusReserve, + pcab->perf)) { + goto error; // error code already filled in + } + + //** Now copy the relevant CFFILE entries to the cabinet temp files. + // For any files which are continued forward from this cabinet, we + // will have to adjust the iFolder field appropriately, and also put + // a copy of the CFFILE entry into the new pfol->tf[iftfCFFOLDER] + // file. This is also the loop where me make the file-placed + // callback. + + if (-1L == _lseek(pfol->tf[iftfCFFOLDER].hf, 0L, SEEK_SET)) { + ErfSetCodes(pcab->perf,FCIERR_TEMP_FILE,errno); + goto error; + } + + while (ReadCFFILEEntry(pcab,pfol)) + { + if (-1 == pcab->pfnfiledest(&pcab->ccab, + pcab->achName, + pcab->cffile.cbFile, + IS_CONTD_BACK(pcab->cffile.iFolder), + pv)) { + ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0); + goto error; + } + + //** If no PREV information, then set iFolder to current number + // within current cabinet. + if (!IS_CONTD_BACK(pcab->cffile.iFolder)) { + pcab->cffile.iFolder = pcab->iFolder; + } + + //** If this file will continue forward, modify the iFolder field + if ( (pcab->cffile.uoffFolderStart + + (UOFF)pcab->cffile.cbFile) > uoffLast) { + if (IS_CONTD_BACK(pcab->cffile.iFolder)) { + pcab->cffile.iFolder = ifoldCONTINUED_PREV_AND_NEXT; + } + else { + pcab->cffile.iFolder = ifoldCONTINUED_TO_NEXT; + } + } + + if (!WriteCount(&pcab->tf[ictfCFFILE], + &pcab->cffile, + sizeof(CFFILE), + pcab->perf) + || !WritePszTmp(&pcab->tf[ictfCFFILE], + pcab->achName, + pcab->perf)) { + goto error; // error code already filled in + } + pcab->cfheader.cFiles++; // count CFFILE entries + + //** Make sure continued-forward files also exist in the next cab + if (IS_CONTD_FORWARD(pcab->cffile.iFolder)) + { + //** need to get rid of the 'continued-forward' setting + pcab->cffile.iFolder = ifoldCONTINUED_FROM_PREV; + + if (!WriteCount(&tf[iftfCFFOLDER], + &pcab->cffile, + sizeof(CFFILE), + pfol->perf) + || !WritePszTmp(&tf[iftfCFFOLDER], + pcab->achName, + pfol->perf)) { + goto error; // error code already filled in + } + } + + } // end while loop reading CFFILE entries + + //** Now nuke the original folder files, and copy the new ones + // into the folder structure. + + if (!NukeTempFiles(&pfol->tf[iftfCFDATA],NUM_FOLDER_TF,pfol->perf)) { + goto error; // error code already filled in + } + + //** Move continuation tempfiles to new folder + pfol->tf[iftfCFDATA] = tf[iftfCFDATA]; // Do structure assignment! + pfol->tf[iftfCFFOLDER] = tf[iftfCFFOLDER]; + + if (fCabinetFull) { + if (!FlushCab(pcab, // Write out cab, update ccab.cb! + pfnProgress, + &cbDone, + cbTot, + pv)) { + goto error; // error already filled in + } + if ((pcab->ccab.cb > 0) && // Space left on current disk + (pcab->ccab.iDisk == pcab->ccabNext.iDisk)) { // Not switching disks + Assert(fGetNextCab); // Should only happen if we were forced + pcab->ccabNext.cb = pcab->ccab.cb; // Use actual space left + } + pcab->ccab = pcab->ccabNext; // Set current cabinet from next cab + pcab->iFolder = 0; // start back at a new folder number + } + } // loop here until pending folder is exhausted. + + //** Don't increment folder number if we forced a GetNextCab call! + if (!fGetNextCab) { + pcab->iFolder++; + } + //** Free resources + (*pcab->pfnfree)(pcfdata); + (*pcab->pfnfree)(pcfdataFirstPart); + (*pcab->pfnfree)(pcffolder); + return TRUE; + +error: + //** Clean up and exit + if (!pcfdata) { + (*pcab->pfnfree)(pcfdata); + } + + if (!pcfdataFirstPart) { + (*pcab->pfnfree)(pcfdataFirstPart); + } + + if (!pcffolder) { + (*pcab->pfnfree)(pcffolder); + } + + return FALSE; +} /* AddFolderToCabinet() */ + + +/*** CabDestroy - flush buffers, close temp files + * + * Entry: + * hcab -- handle to cabinet context + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error filled in + */ +BOOL CabDestroy (HCAB hcab) +{ + PCAB pcab; + BOOL retcode; + + pcab = PCABfromHCAB (hcab); + AssertCAB (pcab); // verify structure signature + + retcode = NukeTempFiles(pcab->tf,NUM_CAB_TF,pcab->perf); +//BUGBUG 15-Mar-1994 bens Need to destroy folder temp files, too? + + ClearAssertSignature(pcab); + pcab->pfnfree(pcab); // free our data + return retcode; // Failure if retcode == FALSE +} + + +/*** ReadCFFILEEntry -- Read in a complete CFFILE entry + * + * Entry: + * pcab - location of cffile struct to fill in + * pfol - get the source file handle from here + * + * Exit success: + * pcab->cffile structure filled in + * pcab->achName filled in + * return code TRUE + * + * Exit failure: + * return code FALSE + * if error is other than EOF, then error struct filled in + */ +BOOL ReadCFFILEEntry(PCAB pcab,PFOL pfol) +{ + int cbRead; + cbRead = _read(pfol->tf[iftfCFFOLDER].hf, + &pcab->cffile, + sizeof(CFFILE)); +//BUGBUG 14-Apr-1994 bens Check for error from _read()! + + if (cbRead == 0) + return FALSE; // no more CFFILE entries! + + if (cbRead != sizeof (CFFILE) + || !ReadPSZ(pfol->tf[iftfCFFOLDER].hf, + pcab->achName, + CB_MAX_FILENAME, + pfol->perf)) + return FALSE; // abort if problem reading filename + + return TRUE; +} + + +/*** ReadCFDATAEntry -- Read in a complete CFDATA entry + * + * Entry: + * pcab - Cabinet context + * pcfdata - CFDATA structure to fill in (Must have RESERVE space!) + * pfol - Folder structure for file handle, buf len, buffer + * + * Exit-Success: + * cfdata structure filled in + * return code TRUE + * + * Exit-Failure: + * return FALSE + * Note: If return code is FALSE, but there is no error + * filled in, then it is a simple EOF which may not + * really be an error + */ +BOOL ReadCFDATAEntry(PCAB pcab,CFDATA *pcfdata,PFOL pfol) +{ + //** Get CFDATA header + if (pcab->cbCFDataPlusReserve != (UINT)_read(pfol->tf[iftfCFDATA].hf, + pcfdata, + pcab->cbCFDataPlusReserve)) { +//BUGBUG 14-Apr-1994 bens Check for error from _read()! + return FALSE; // no more entries in file! + } + + //** Make sure compressed data block size is not larger than when + // we created it the first time! + if (pcfdata->cbData > pfol->cbDstBuffer) { + Assert(0); + return 0; // bogus chunk size! + } + + //** Get actual data + if ((pcfdata->cbData != (UINT)_read(pfol->tf[iftfCFDATA].hf, + pfol->pchCompr, + pcfdata->cbData))) { + ErfSetCodes(pfol->perf,FCIERR_TEMP_FILE,errno); + return FALSE; // Didn't get all data we asked for + } + + return TRUE; // return success! +} + + +/*** ReadPSZ -- Read in a psz name + * + * Entry: + * hf - file handle to read from + * pb - buffer to load name into + * cb - size of buffer + * perf - error return structure + * + * Exit success: + * returns TRUE; name filled in + * + * Exit failure: + * returns FALSE; error filled in (read error, or string too long) + */ +BOOL ReadPSZ(int fh, char *pb, int cb, PERF perf) +{ + char chLast; + int cbValue; + int cbRead; + long pos; + + pos = _lseek(fh,0,SEEK_CUR); // Save current position + + //** Read in enough to get longest possible value + cbRead = _read(fh,pb,cb); + if (cbRead <= 0) { // At EOF, or an error occured + ErfSetCodes(perf,FCIERR_TEMP_FILE,errno); + return FALSE; + } + + //** Pick out just ASCIIZ string and adjust file position + chLast = pb[cb-1]; // Save last character + pb[cb-1] = '\0'; // Ensure terminated + cbValue = strlen(pb); // Get string length + if ( ((cbValue+1) >= cb) && (chLast != '\0')) { + //** String filled up buffer and was not null terminated in + // file, so something must be wrong. + ErfSetCodes(perf,FCIERR_TEMP_FILE,errno); + return FALSE; + } + + _lseek(fh,pos+cbValue+1,SEEK_SET); // Position to just past string + return TRUE; +} + + +/*** FlushCab - Combine the pcab-tf files into a real cabinet file + * + * Entry: + * pcab - cabinet context structure + * pfnProgress - status callback function + * pcbDone - pointer to bytes Done accumulator + * cbTot - total bytes in AddFolder operation + * pv - original caller's context for callbacks + * + * Exit-success: + * returns TRUE; pcab->ccab.cb updated with size of cabinet desired + * by client. + * + * Exit-failure: + * returns FALSE, error struct filled in + */ +BOOL FlushCab(PCAB pcab, + PFNFCISTATUS pfnProgress, + unsigned long *pcbDone, + unsigned long cbTot, + void *pv) +{ + USHORT cb; + long cbCabinetActual; // Actual cabinet size + long cbCabinetClient; // Size client wants us to use + USHORT cbTotal; + CFFOLDER *pcffolder=NULL; + COFF coffFolderBase; + int hf=-1; + CFRESERVE *pcfreserve; + char szDestFilename[CB_MAX_FILENAME]; // Full name of dest cab + + //** Construct cabinet file name + strcpy(szDestFilename,pcab->ccab.szCabPath); + strcat(szDestFilename,pcab->ccab.szCab); + + if (-1 == (hf=_open(szDestFilename, + _O_CREAT | _O_TRUNC | _O_BINARY | _O_RDWR, + _S_IREAD | _S_IWRITE ))) { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + goto error; + } + + //** Allocate buffer for reading/writing CFFOLDER structure. + // Due to the RESERVE field, this is not a fixed sized buffer! + + if (!(pcffolder = (*pcab->pfnalloc)(pcab->cbCFFolderPlusReserve))) { + goto error; + } + + //** Begin computation of start of CFFOLDERs + coffFolderBase = pcab->cbCFHeaderPlusReserve; + + //** Figure out existence of previous/next disk/cabinet info + if (pcab->achCabinetFirst[0] != '\0') { + pcab->cfheader.flags |= cfhdrPREV_CABINET; // Set previous flag + coffFolderBase += 1+strlen(pcab->achCabinetFirst) + + 1+strlen(pcab->achDiskFirst); + } + if (pcab->achCabinetNext[0] != '\0') { + pcab->cfheader.flags |= cfhdrNEXT_CABINET; // Set previous flag + coffFolderBase += 1+strlen(pcab->achCabinetNext) + + 1+strlen(pcab->achDiskNext); + } + + //** Account for CFFOLDER structures + pcab->cfheader.coffFiles = coffFolderBase + pcab->tf[ictfCFFOLDER].cb; + + //** Set overall file size + pcab->cfheader.cbCabinet = pcab->cfheader.coffFiles + +pcab->tf[ictfCFFILE].cb + +pcab->tf[ictfCFDATA].cb; + + //** Make sure RESERVE flag is set correctly + if (pcab->cbReserveCF > 0) { + pcab->cfheader.flags |= cfhdrRESERVE_PRESENT; // Set flag + } + + //** Store setID and cabinet number + pcab->cfheader.setID = pcab->setID; + pcab->cfheader.iCabinet = pcab->iCabinet; + pcab->iCabinet++; // Increment for next cabinet + + //** write out CFHEADER + if (sizeof(CFHEADER) != _write(hf,&pcab->cfheader,sizeof(CFHEADER))) + { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + return FALSE; + } + + //** Write out CFRESERVE section (if present) for CFHEADER + if (pcab->cbReserveCF > 0) { + //** Fill in CFRESERVE + pcfreserve = (CFRESERVE *)pcab->abReserve; + pcfreserve->cbCFHeader = (USHORT)pcab->ccab.cbReserveCFHeader; + pcfreserve->cbCFFolder = (BYTE)pcab->ccab.cbReserveCFFolder; + pcfreserve->cbCFData = (BYTE)pcab->ccab.cbReserveCFData; + + //** Write out CFRESERVE + if (sizeof(CFRESERVE) != _write(hf,pcab->abReserve,sizeof(CFRESERVE))) + { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + goto error; + } + + //** Zero RESERVE data area + memset(pcab->abReserve,0,sizeof(pcab->abReserve)); + cbTotal = (USHORT)(pcab->cbReserveCF - sizeof(CFRESERVE)); + while (cbTotal > 0) { + //** Figure out how much to write + if (cbTotal > sizeof(pcab->abReserve)) { + cb = sizeof(pcab->abReserve); + } + else { + cb = cbTotal; + } + //** Write out buffer + if (cb != (USHORT)_write(hf,pcab->abReserve,cb)) + { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + goto error; + } + //** Adjust count of remaining bytes to write + cbTotal -= cb; + } + } + + //** Write out previous/next cabinet/disk strings + if (pcab->cfheader.flags & cfhdrPREV_CABINET) + { + if (!WritePsz(hf, + pcab->achCabinetFirst, + pcab->perf) + ||!WritePsz(hf, + pcab->achDiskFirst, + pcab->perf)) { + goto error; // error code already filled in + } + } + + if (pcab->cfheader.flags & cfhdrNEXT_CABINET) + { + if (!WritePsz(hf, + pcab->achCabinetNext, + pcab->perf) + || !WritePsz(hf, + pcab->achDiskNext, + pcab->perf)) { + goto error; // error code already filled in + } + } + pcab->achCabinetNext[0]=0; // cancel next-cab links for now + pcab->achDiskNext[0]=0; + strcpy(pcab->achCabinetFirst,pcab->achNxtCabFirst); // update back pointers + strcpy(pcab->achDiskFirst,pcab->achNxtDskFirst); + + //** Rewind CFFOLDER temporary file + if (-1 == _lseek(pcab->tf[ictfCFFOLDER].hf, 0L, SEEK_SET)) + { + ErfSetCodes(pcab->perf,FCIERR_TEMP_FILE,errno); + goto error; + } + + //** We must manually copy the CFFOLDER records, because they need to + // have the start of the CFDATA area added to the coffCabStart fields. + + while (pcab->cbCFFolderPlusReserve == + (UINT)_read(pcab->tf[ictfCFFOLDER].hf, + pcffolder, + pcab->cbCFFolderPlusReserve)) { +//BUGBUG 14-Apr-1994 bens Check for error from _read()! + //** Adjust folder coffCabStart field + pcffolder->coffCabStart += (coffFolderBase + + pcab->tf[ictfCFFOLDER].cb + // size of CFFOLDERs + pcab->tf[ictfCFFILE].cb); // size of CFFILEs + if (pcab->cbCFFolderPlusReserve != + (UINT)_write(hf,pcffolder,pcab->cbCFFolderPlusReserve)) { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + return FALSE; + } + } + + //** Copy the CFFILE records to the cabinet file + if (!filecopy(hf, + pcab->tf[ictfCFFILE].hf, + pfnProgress, + pcbDone, + cbTot, + pv, + pcab->perf)) { + goto error; + } + + //** Copy the CFDATA records to the cabinet file + if (!filecopy(hf, + pcab->tf[ictfCFDATA].hf, + pfnProgress, + pcbDone, + cbTot, + pv, + pcab->perf)) { + goto error; + } + + //** Get actual size of cabinet for callback + cbCabinetActual = _lseek(hf,0,SEEK_END); + if (-1 == cbCabinetActual) { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + goto error; + } + + //** Close the file + if (-1 == _close(hf)) { + ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno); + goto error; + } + + //** Get rid of the temporary files + if (!NukeTempFiles(pcab->tf,NUM_CAB_TF,pcab->perf) + || !CrTempFiles (pcab->tf,NUM_CAB_TF,pcab->pfntemp,pcab->perf)) { + goto error; // error already filled in + } + + //** Tell client true size of cabinet, get clients desired size + if (-1 == (cbCabinetClient = pfnProgress(statusCabinet, + pcab->cbCabinetEstimate, + cbCabinetActual, + pv))) { + ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,errno); + return FALSE; + } + /* + * Update maximum cabinet size using clients desired size. + * + * NOTE: Either we flushed the current cabinet because it had exceeded + * its maximum size (in which case the following code should + * reduce pcab->ccab.cb to 0), or because of an explicit + * FCIFlushCabinet() call (in which case there may be some + * space left. + */ + Assert(cbCabinetClient >= cbCabinetActual); // At least as much as actual + Assert(cbCabinetClient <= (long)pcab->ccab.cb); // No bigger than max + pcab->ccab.cb -= cbCabinetClient; // Shrink limit + + //** Reinitialize header + InitCFHEADER(&pcab->cfheader); + + //** Free resources + (*pcab->pfnfree)(pcffolder); + return TRUE; + +error: + if (!pcffolder) { + (*pcab->pfnfree)(pcffolder); + } + +//BUGBUG 15-Mar-1994 bens Close (and delete?) temp files? + + if (hf != -1) { // File was created + _close(hf); // Close it +#ifndef ASSERT // Leave dregs behind in assert build to help diagnostics + _unlink(szDestFilename); // Delete it +#endif + } + + return FALSE; +} + + +/*** filecopy -- copy a cab temp file into the final cabinet + * + * Entry: + * hfdst - cab file handle + * hfsrc - temp file handle + * pfnProgress - status callback + * pcbDone - bytes done for status callback + * cbTot - total bytes for status callback + * pv - context for callback + * perf - error return structure + * + * Exit-success: + * returns TRUE; + * + * Exit-failure: + * returns FALSE, error structure filled in + * + */ +BOOL filecopy(int hfdst, + int hfsrc, + PFNFCISTATUS pfnProgress, + unsigned long *pcbDone, + unsigned long cbTot, + void *pv, + PERF perf) +{ + UINT cb; +#ifdef BIT16 +#define cbFILE_COPY 1024 // Scrimp on memory in real-mode +#else +#define cbFILE_COPY 32768 // Pork out a little to speed up I/O +#endif + static xbuf[cbFILE_COPY]; + + if (-1 == _lseek(hfsrc, 0L, SEEK_SET)) { + ErfSetCodes(perf,FCIERR_TEMP_FILE,errno); + return FALSE; + } + + while (cb = _read(hfsrc,xbuf,sizeof(xbuf))) + { + if (cb != (unsigned)_write(hfdst,&xbuf,cb)) + { + ErfSetCodes(perf,FCIERR_CAB_FILE,errno); + return FALSE; + } + *pcbDone += cb; + if (-1 == pfnProgress(statusFolder, + *pcbDone, + cbTot, + pv)) { + ErfSetCodes(perf,FCIERR_USER_ABORT,errno); + return FALSE; + } + } + + return TRUE; +} /* filecopy() */ + + +/*** InitCFHEADER -- initialize a CFHEADER to a fresh state + * + * Entry: + * pcfheader - pointer to an allocated CFHEADER + * + * Exit: + * all fields initialized + */ +void InitCFHEADER( CFHEADER *pcfheader) +{ + pcfheader->sig = sigCFHEADER; + pcfheader->csumHeader = 0; + pcfheader->cbCabinet = 0; + pcfheader->version = verCF; // Set cabinet version number + pcfheader->cFolders = 0; + pcfheader->cFiles = 0; + pcfheader->flags = 0; + pcfheader->csumFolders = 0; + pcfheader->coffFiles = 0; + pcfheader->csumFiles = 0; +} + + +/*** WritePsz - Writes a psz string to cabinet output file + * + * Entry: + * hf - pointer to temp file structure + * psz - string to write + * perf - error structure + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error set to FCIERR_CAB_ERR + */ +BOOL WritePsz(int hf, char *psz,PERF perf) +{ + if ((1+strlen(psz)) != (unsigned) _write(hf,psz,1+strlen(psz))) + { + ErfSetCodes(perf,FCIERR_CAB_FILE,errno); + return FALSE; + } + return TRUE; +} diff --git a/private/windows/diamond/chuck/buildcab.h b/private/windows/diamond/chuck/buildcab.h new file mode 100644 index 000000000..0ab0b1a0b --- /dev/null +++ b/private/windows/diamond/chuck/buildcab.h @@ -0,0 +1,313 @@ +/*** buildcab.h - Diamond cabinet/folder builder functions + * + * Author: + * Chuck Strouss + * + * Note: + * Since this file is intended to be used only by functions internal + * to the high-level FCI, it contains the details of the context + * structures. + * + * History: + * 29-Nov-1993 chuckst Created + * 30-Nov-1993 chuckst Renamed, switched to handle based method. + * 01-Dec-1993 chuckst Now requires only 2 output files + * 01-Dec-1993 chuckst Added switchable compression contexts + * 03-Dec-1993 chuckst Actually implemented compression contexts + * 15-Mar-1994 bens Add enumeration for temp file indicies + * 22-Mar-1994 bens Rename spiff-up + * 01-Apr-1994 bens Implement support for .New Cabinet + * 18-May-1994 bens Add Chicago M6 hack + */ + +#ifndef INCLUDED_BUILDCAB +#define INCLUDED_BUILDCAB 1 + +typedef struct { /* tf */ // temp file + unsigned long cb; // current size + int hf; // file handle + char szName[CB_MAX_FILENAME]; // temp file name +} TF; + + +typedef enum { + ictfCFDATA, // 0 => CFDATA temp file + ictfCFFILE, // 1 => CFFILE temp file + ictfCFFOLDER, // 2 => CFFOLDER temp file +} INDEX_CABINET_TEMP_FILE; /* ictf */ +#define NUM_CAB_TF 3 // 3 temp files per open cabinet + + +typedef struct { /* cab */ +#ifdef ASSERT + SIGNATURE sig; // structure signature sigCAB +#endif + UOFF uoffNext; + PFNFCIFILEPLACED pfnfiledest; // callback to notify of file placements + PFNFCIGETTEMPFILE pfntemp; // callback to get temporary filenames + PFNALLOC pfnalloc; // memory alloc function + PFNFREE pfnfree; // memory free function + PERF perf; + long cbCabinetEstimate; // Estimated size of cabinet + +// Next come the nested structures. + + TF tf[NUM_CAB_TF]; // temp files + + CCAB ccab; // cabinet/disk name struct + CCAB ccabNext; + CFHEADER cfheader; // build the CFHEADER here + CFFILE cffile; // CFFILE buffer + + BYTE abReserve[256]; // Temp buffer for writing RESERVE +#if (256 < cbRESERVE_FOLDER_MAX) || (256 < cbRESERVE_DATA_MAX) +#error ccab.abReserve[] is too small +#endif + + UINT cbReserveCF; // Size of RESERVEd area in CFHEADER + UINT cbCFHeaderPlusReserve; // Size of CFHEADER + header RESERVE + UINT cbCFFolderPlusReserve; // Size of CFFOLDER + folder RESERVE + UINT cbCFDataPlusReserve; // Size of CFDATA + data RESERVE + +// Shorts come next + + USHORT iFolder; + USHORT setID; // Cabinet set ID + USHORT iCabinet; // Cabinet number + +// And the chars come last + + char achName [CB_MAX_FILENAME +1]; // for filenames + + char achCabinetFirst[CB_MAX_CABINET_NAME+1]; + char achDiskFirst [CB_MAX_DISK_NAME +1]; + char achNxtCabFirst [CB_MAX_CABINET_NAME+1]; + char achNxtDskFirst [CB_MAX_DISK_NAME +1]; + char achCabinetNext [CB_MAX_CABINET_NAME+1]; + char achDiskNext [CB_MAX_DISK_NAME +1]; + +} CAB; +typedef CAB *PCAB; /* pcab */ + +typedef void *HCAB; // handle to cabinet being built + +#define PCABfromHCAB(hcab) ((PCAB)(hcab)) +#define HCABfromPCAB(pcab) ((HCAB)(pcab)) + + +#ifdef ASSERT +#define sigCAB MAKESIG('C','A','B','I') // CAB signature +#define AssertCAB(pcab) AssertStructure(pcab,sigCAB); +#else // !ASSERT +#define AssertCAB(pcab) +#endif // !ASSERT + +typedef enum { + iftfCFDATA, // 0 => CFDATA temp file + iftfCFFOLDER, // 1 => CFFOLDER temp file +} INDEX_FOLDER_TEMP_FILE; /* iftf */ + +#define NUM_FOLDER_TF 2 // just two temp files, CFF and CFD + +typedef struct { /* fol */ + +// (long) stuff comes first + +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFOL +#endif + UOFF uoffNext; + MCI_CONTEXT_HANDLE mch; + PFNALLOC pfnalloc; // memory allocation function + PFNFREE pfnfree; // memory free function + PERF perf; + + TF tf[NUM_FOLDER_TF]; + +//** Time for the ints + + UINT cbCFDataPlusReserve; // Size of CFDATA + data RESERVE + UINT cbDstBuffer; // size of compressed data buffer + UINT cbSrcBuffer; // size of uncompressed data buffer + UINT cbBuffered; +#ifndef REMOVE_CHICAGO_M6_HACK + int fFailOnIncompressible; // TRUE => Fail if a block + // is incompressible +#endif + TCOMP typeCompress; + +//** The chars come last + + char *pchUncompr; + char *pchCompr; +} FOL; +typedef FOL *PFOL; /* pfol */ + +#define PFOLfromHFOL(hfol) ((PFOL)(hfol)) +#define HFOLfromPFOL(pfol) ((HFOL)(pfol)) + + +#ifdef ASSERT +#define sigFOL MAKESIG('F','O','L','D') // FOL signature +#define AssertFOL(pfol) AssertStructure(pfol,sigFOL); +#else // !ASSERT +#define AssertFOL(pfol) +#endif // !ASSERT + + +typedef void *HFOL; /* hfol - Handle to folder builder context */ + + +/*** FolderInit - open temp files for folder building, init pointers + * + * Entry: + * folerr - Structure where we return error codes + * pfnalloc - Memory allocation function callback + * pfnfree - Memory free function callback + * pfntemp - Tempfile name function callback + * cbCFDataPlusReserve - Total size of a CFDATA + reserved area + * + * Note: The alloc/free callbacks must remain valid throughout + * the life of the handle, up to and including the + * final delete call. + * + * If there is an error, the context will not be created and the + * returned handle will be a NULL. In this case, the folerr structure + * will be filled in with a description of the error. + */ +HFOL FolderInit(ERF *folerr, + PFNALLOC pfnalloc, + PFNFREE pfnfree, + PFNFCIGETTEMPFILE pfntemp, + UINT cbCFDataPlusReserve + ); + + +/*** AddFileToFolder - Add a disk file to a folder + * + * Entry: + * hfol - Compression context handle + * pszSourceFile - Name of file to add to folder + * pszFileName - Name to store into folder/cabinet + * pfnProgress - Progress callback + * fCompress - If this is false, the file will be added uncompressed. + * pv - Caller's context for callbacks + * + * Notes: + * This function is used to build folders. The output goes into + * three open files, one of which has the actual data fields. The + * other two files contain the relevant CFFILE and CFDATA blocks + * for this folder. When all folders for a given cabinet are + * complete, the relevant parts of the folders can be copied + * together into the cabinet file. + * + * Note that the folder context can be saved by duplicating + * the compression context with an FCI call, and by saving the + * length of each of the temp files. To rewind, go back to + * the original compression context and truncate the files. + */ +int AddFileToFolder( + HFOL hfol, // FCI compression context + char *pszSourceFile,// filename to get new data from + char *pszFileName, // name to store into CAB file + BOOL fExecute, // Execute after extracting + PFNFCISTATUS pfnProgress, // progress callback + PFNFCIGETOPENINFO pfnOpenInfo, // open file, get info callback + TCOMP typeCompress, // compression type + void *pv); // caller's context for callbacks + + +/*** FolderFlush - flush out any remaining folder buffers + * and reset the MCI compressor + * + * Entry: + * hfol - folder context + * pfnProgress - Progress report callback + * pv - pointer to caller's context + * + * Exit Success: + * return code is zero + * + * Exit Failure: + * return code is non-zero + * error structure (passed in to init call) filled in + */ +BOOL FolderFlush (HFOL hfol, + PFNFCISTATUS pfnProgress, + void *pv); + + +/*** SetCompressionType -- initializes a new compressor + * + * Entry: + * typeCompress -- new compression type + * pfol -- folder context structure + * + * Exit-success: + * returns TRUE; + * + * Exit-failure: + * returns FALSE; + */ +BOOL SetCompressionType(TCOMP typeCompress, + PFOL pfol); + +/*** FolderDestroy - Destroy a folder context and close temp files + * + * Entry: + * hfol -- folder context + * + * Exit-success: + * return code 0 + * + * Exit-failure: + * return code 1, error structure filled in + */ +BOOL FolderDestroy (HFOL hfol); + + +HCAB CreateCab( + char *pszFileName, // name for .CAB files + PERF fcierr, // error type return structure + PFNFCIFILEPLACED pfnfiledest, // callback for file placement + PFNALLOC pfnalloc, // memory allocation function + PFNFREE pfnfree, // memory free function + PFNFCIGETTEMPFILE pfntemp); + +BOOL AddFolderToCabinet( + PCAB pcab, // open cabinet context + PFOL pfol, // open folder context + BOOL fGetNextCab,// TRUE => force GetNextCab + PFNFCIGETNEXTCABINET GetNextCab, // Get next cabinet callback + PFNFCISTATUS pfnProgress,// progress callback + void *pv); // caller's context + +BOOL FlushCab( + PCAB pcab, + PFNFCISTATUS pfnProgress, + unsigned long *pcbDone, + unsigned long cbTot, + void *pv + ); + +BOOL CabDestroy(HCAB hcab); // go ahead and build the cabinet + +BOOL CrTempFiles(TF *ptf, + int ctf, + PFNFCIGETTEMPFILE pfntemp, + PERF perf); + +BOOL WritePszTmp(TF *ptf, + char *psz, + PERF perf); + +BOOL WriteCount(TF *ptf, + const void *pv, + unsigned int cb, + PERF perf); + +BOOL NukeTempFiles(TF *ptf, + int ctf, + PERF perf); + +#endif // INCLUDED_BUILDCAB diff --git a/private/windows/diamond/chuck/buildfol.c b/private/windows/diamond/chuck/buildfol.c new file mode 100644 index 000000000..93378aa42 --- /dev/null +++ b/private/windows/diamond/chuck/buildfol.c @@ -0,0 +1,764 @@ +/*** buildfol.c - Diamond Folder Builder + * + * Author: + * Chuck Strouss + * + * History: + * 01-Dec-1993 chuckst Initial version + * 01-Dec-1993 chuckst Now requires only two output files + * 03-Dec-1993 chuckst Implemented FCC blocks + * 09-Jan-1994 chuckst Renamed from FCI.C, FCI.C will be the + * higher level calls. + * 15-Mar-1994 bens Add tempfile index enumeration; RESERVE work + * 01-Jun-1994 bens Add Quantum support + */ + +#include <memory.h> +#include <string.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <stdio.h> +#include <errno.h> + +#include "types.h" +#include "asrt.h" +#include "fci_int.h" +#include "cabinet.h" +#include "erf.h" +#include "..\mszip\mci.h" +#include "..\quantum\qci.h" + +#include "buildcab.h" +#include "checksum.h" + + +//*** Internal function prototypes + +BOOL WriteCFDATABlock (PFOL pfol, + PFNFCISTATUS pfnProgress, + void *pv); + +BOOL MCIResetCompressionGlobal(PFOL pfol); +BOOL MCICreateCompressionGlobal(PFOL pfol); +BOOL MCIDestroyCompressionGlobal(PFOL pfol); +BOOL MCICompressGlobal(PFOL pfol, USHORT *pcbData); + + +/*** FolderInit - Initialize for folder creation + * + */ +HFOL FolderInit(PERF perf, // error type return structure + PFNALLOC pfnalloc, // memory allocation function + PFNFREE pfnfree, // memory free function + PFNFCIGETTEMPFILE pfntemp, // generate temp file name + UINT cbCFDataPlusReserve + ) +{ + PFOL pfol; + + pfol = (PFOL)pfnalloc(sizeof (FOL)); + if (pfol == NULL) { + ErfSetCodes(perf,FCIERR_ALLOC_FAIL,0); + return NULL; + } + + SetAssertSignature(pfol,sigFOL); + AssertFOL(pfol); // Make sure we've really set sig + + pfol->perf = perf; // Save away error pointer + pfol->pfnfree = pfnfree; // Save free function in our context + pfol->pfnalloc = pfnalloc; // Save alloc function in our context + pfol->pchCompr = NULL; // Simplify error cleanup + pfol->pchUncompr = NULL; // Simplify error cleanup + pfol->typeCompress = tcompBAD; // No compressor initialized yet + + pfol->cbCFDataPlusReserve = cbCFDataPlusReserve; + + if (!CrTempFiles(pfol->tf,NUM_FOLDER_TF,pfntemp,pfol->perf)) { + FolderDestroy(pfol); // clean up + return NULL; + } + + pfol->uoffNext = 0; // keep track of uncompressed size. + pfol->cbBuffered = 0; // Nothing currently in our buffer + + return HFOLfromPFOL(pfol); +} /* FolderInit() */ + + +/*** AddFileToFolder -- add a source file into the current folder + * + * Entry: + * parameters noted below + * + * Exit success: + * Returns TRUE + * + * Exit failure: + * Returns FALSE + * error structure indicates error type + * files closed, context deleted + */ +int AddFileToFolder( + HFOL hfol, // compression context + char *pszSourceFile, // filename to get new data from + char *pszFileName, // name to store into CAB file + BOOL fExecute, // Set Execute on extract + PFNFCISTATUS pfnProgress, // Progress callback + PFNFCIGETOPENINFO pfnOpenInfo, // Open file, get info callback + TCOMP typeCompress, // compression setting + void *pv) // context pointer for callbacks +{ + PFOL pfol; + int hfInput; + int cbRead; + CFFILE cff; + + pfol = PFOLfromHFOL(hfol); + AssertFOL (pfol); // verify structure signature + cff.uoffFolderStart = pfol->uoffNext; // save file start + cff.iFolder = 0; // folder numbers filled in at + + if (-1 == (hfInput = pfnOpenInfo(pszSourceFile, + &cff.date, + &cff.time, + &cff.attribs, + pv))) { + ErfSetCodes(pfol->perf,FCIERR_OPEN_SRC,errno); // set error + return FALSE; // fatal error + } + + // Overloading this flag -- if it's actually used we're in trouble + Assert( !(cff.attribs & RUNATTRIB) ); + if (fExecute) { + cff.attribs |= RUNATTRIB; + } + + while ((cbRead = _read(hfInput, + &(pfol->pchUncompr[pfol->cbBuffered]), + pfol->cbSrcBuffer-pfol->cbBuffered)) !=0 ) { + if (cbRead == -1) { // Read error + ErfSetCodes(pfol->perf,FCIERR_READ_SRC,errno); + goto error; + } + pfol->uoffNext += (unsigned)cbRead; + pfol->cbBuffered += (unsigned)cbRead; + //** If we filled up the buffer, compress it and write it + if (pfol->cbBuffered == pfol->cbSrcBuffer) { + // Compress, write, give progress reports + if (!WriteCFDATABlock(pfol,pfnProgress,pv)) { + goto error; // error already filled in + } + } + } + //** Done reading this file + _close(hfInput); + + //** Write out the CFFILE structure to the temp file + cff.cbFile = pfol->uoffNext - cff.uoffFolderStart; + if (!WriteCount(&pfol->tf[iftfCFFOLDER], + &cff, + sizeof(cff), + pfol->perf) + || !WritePszTmp(&pfol->tf[iftfCFFOLDER], + pszFileName, + pfol->perf)) { + goto error; + } + + //** Success + return TRUE; + +error: + if (hfInput != -1) { + _close(hfInput); + } + return FALSE; +} + + +/*** FolderFlush - Flush buffers, reset MCI compressor + * + * Entry: + * hfol -- handle to folder context + * pfnProgress -- Progress callbacks go here + * pv -- original caller's callback context + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE + * error structure in pfol filled in + */ +BOOL FolderFlush (HFOL hfol,PFNFCISTATUS pfnProgress,void *pv) +{ + PFOL pfol; + BOOL rc; + + pfol = PFOLfromHFOL (hfol); + AssertFOL (pfol); + + //** flush the last buffer, if any + rc = WriteCFDATABlock(pfol,pfnProgress,pv); + if (!MCIResetCompressionGlobal(pfol)) { // reset the compressor + return FALSE; // error already filled in + } + + pfol->uoffNext = 0; // keep track of uncompressed size. + return rc; // error already filled in +} + + +/*** FolderDestroy - Deletes temp files and destroy a folder context + * + * Entry: + * hfol -- folder context + * + * Exit-Success: + * returns TRUE + * + * Exit-Failure: + * returns FALSE, error struct filled in + */ +BOOL FolderDestroy (HFOL hfol) +{ + + PFOL pfol; + BOOL fOK; + + pfol = PFOLfromHFOL (hfol); + AssertFOL (pfol); + + SetCompressionType(tcompBAD,pfol); // clear out compressor + + fOK = NukeTempFiles(pfol->tf,NUM_FOLDER_TF,pfol->perf); + + ClearAssertSignature(pfol); + (*pfol->pfnfree)(pfol); + return fOK; +} + + +/*** SetCompressionType -- initializes a new compressor + * + * Entry: + * typeCompress - New compression type (tcompBAD to term w/ no new) + * pfol - Folder context structure + * + * Exit-Success: + * returns TRUE; + * + * Exit-Failure: + * returns FALSE, error structure filled in + */ +BOOL SetCompressionType(TCOMP typeCompress,PFOL pfol) +{ +//BUGBUG 15-Mar-1994 bens What if new type is same as old type? + if (!MCIDestroyCompressionGlobal(pfol)) { + return FALSE; // error already filled in + } + + pfol->typeCompress = typeCompress; // update current compression type + + if (!MCICreateCompressionGlobal(pfol)) { + return FALSE; // error already filled in + } + + return TRUE; +} + + +/*** WriteCFDATABlock - write out a CFDATA block to current .CFD file + * + * Entry: + * pfol - pointer to folder structure + * pfnProgress - issue Progress callbacks to this function + * pv - context to pass to Progress callbacks + * + * Exit-Success: + * Returns TRUE + * + * Exit-Failure: + * Returns FALSE; error structure filled in + */ +BOOL WriteCFDATABlock (PFOL pfol, PFNFCISTATUS pfnProgress, void *pv) +{ + CFDATA *pcfdata=NULL; + + AssertFOL (pfol); + + if (pfol->cbBuffered == 0) { + return TRUE; // done if nothing to write + } + + if (!(pcfdata = (*pfol->pfnalloc)(pfol->cbCFDataPlusReserve))) { + ErfSetCodes(pfol->perf,FCIERR_ALLOC_FAIL,0); + return FALSE; + } + //** Zero reserved area (and rest of CFDATA structure, too) + memset(pcfdata,0,pfol->cbCFDataPlusReserve); + + if (!MCICompressGlobal(pfol,&(pcfdata->cbData))) { + goto error; // error struct already filled in + } + +// Generate the CFDATA structure that will be prepended to the actual +// data on the disk. Notice that we will not fill in the checksum until +// later, when this record is copied to the cabinet. This works better +// because there are times when a CFDATA block ends up being split +// between two cabinets. + + pcfdata->cbUncomp = pfol->cbBuffered; + + if (!WriteCount(&pfol->tf[iftfCFDATA], + pcfdata, + pfol->cbCFDataPlusReserve, + pfol->perf) + || !WriteCount(&pfol->tf[iftfCFDATA], + pfol->pchCompr, + pcfdata->cbData, + pfol->perf)) { + goto error; // Error already filled in + } + + if (-1 == pfnProgress(statusFile, // type of Progress callback + pcfdata->cbData, // Compressed size + pfol->cbBuffered, // Uncompressed size + pv)) { // context pointer + ErfSetCodes(pfol->perf,FCIERR_USER_ABORT,0); + goto error; + } + + pfol->cbBuffered = 0; // Folder is empty + (*pfol->pfnfree)(pcfdata); // Free resources + return TRUE; + +error: + //** Clean up and exit + if (pcfdata) { + (*pfol->pfnfree)(pcfdata); + } + return FALSE; +} + + +// The following routines are use for manipulating temporary files + +/*** CrTempFiles -- creates temporary files and opens them for writing + * + * Entry: + * ptf -- pointer to an array of temp file structures + * ctf -- count of temp files to init + * pfntemp -- callback for generating a temp filename + * perf -- error code structure + * + * Exit-success: + * return value is TRUE + * ptf->szName has name of open file + * ptf->hf has file handle + * ptf->cb length field init'd to 0 + * + * Exit-failure: + * return code FALSE, error structure filled in + * + */ + +BOOL CrTempFiles(TF *ptf, + int ctf, + PFNFCIGETTEMPFILE pfntemp, + PERF perf) +{ + int tfIndex; + int iRetry; + + for (tfIndex=0; tfIndex<ctf; tfIndex++) { + ptf[tfIndex].hf = -1; // init all temp file handles to -1 + ptf[tfIndex].cb = 0; // length == empty + } + + for (tfIndex=0; tfIndex< ctf;tfIndex++) + { + + iRetry = 11; // Try 10 times to create a temp file + while ((ptf[tfIndex].hf == -1) && iRetry--) { + if (pfntemp(ptf[tfIndex].szName,sizeof(ptf[tfIndex].szName))) { + //** Create file, must not already exists + ptf[tfIndex].hf = _open(ptf[tfIndex].szName, + _O_CREAT | _O_EXCL | _O_BINARY | _O_RDWR, + _S_IREAD | _S_IWRITE ); + } + } + //** Were we able to create the temp file? + if (ptf[tfIndex].hf == -1) { + ErfSetCodes(perf,FCIERR_TEMP_FILE,errno); + goto error; + } + } + + //** Success + return TRUE; + +error: + //** Close and destroy any files we created + for (tfIndex=0; tfIndex<ctf; tfIndex++) { + if (ptf[tfIndex].hf != -1) { + _close(ptf[tfIndex].hf); + _unlink(ptf[tfIndex].szName); + } + } + return FALSE; +} + + +/*** NukeTempFiles -- close and delete open temp files + * + * Entry: + * ptf -- points to an open temp file structure array + * ctf -- number of files to nuke + * perf -- error structure + * + * Exit-success: + * return TRUE + * temp files closed and deleted + * + * Exit-failure: + * return FALSE, error structure filled in + */ +BOOL NukeTempFiles(TF *ptf,int ctf,PERF perf) +{ + + BOOL fOK=TRUE; + int tfIndex; + + //** Close and delete temp files; we record an error occurence, but + // continue going because we want to close and delete as many as + // possible. + for (tfIndex=0; tfIndex<ctf; tfIndex++) + { + if (_close(ptf[tfIndex].hf)) { + ErfSetCodes(perf,FCIERR_TEMP_FILE,errno); + fOK = FALSE; + } + ptf[tfIndex].hf = -1; // file no longer open + + if (-1 == _unlink(ptf[tfIndex].szName)) { + ErfSetCodes(perf,FCIERR_TEMP_FILE,errno); + fOK = FALSE; + } + } + + return fOK; +} + + +/*** WriteCount -- Write into temp file, count bytes + * + * Entry: + * ptf - pointer to temp file structure + * pv - buffer pointer + * cb - count to write + * perf - error structure + * + * Exit-Success: + * Returns TRUE if the write occurred properly + * + * Exit-Failure: + * Returns FALSE, error struct fille din + */ +int WriteCount(TF *ptf, + const void *pv, + unsigned int cb, + PERF perf) +{ + ptf->cb += cb; // keep track of how much we've written + if (cb != (unsigned)_write(ptf->hf,pv,cb)) + { + ErfSetCodes(perf,FCIERR_TEMP_FILE,0); + return FALSE; + } + return TRUE; +} + + +/*** WritePszTmp -- writes a psz string to temp file + * + * Entry: + * ptf - pointer to temp file structure + * psz - string to write + * perf - error structure + * + * Exit-Success: + * returns TRUE + * + * Exit-Failure: + * returns FALSE, error struct filled in (TEMP file error) + */ +BOOL WritePszTmp(TF *ptf,char *psz,PERF perf) +{ + return WriteCount(ptf,psz,1+strlen(psz),perf); +} + + +/*** MCIDestroyCompressionGlobal -- Destroy the currently selected compressor + * + * Entry: + * pfol - Pointer to folder context + * + * Exit-Success: + * returns TRUE + * + * Exit-Failure: + * returns FALSE, error structure filled in + */ +BOOL MCIDestroyCompressionGlobal(PFOL pfol) +{ + BOOL fOK=TRUE; + + switch(CompressionTypeFromTCOMP(pfol->typeCompress)) { + case tcompBAD: // no existing compression + break; // nothing to do + + case tcompTYPE_NONE: + break; //no action needed for null compressor + + case tcompTYPE_MSZIP: + if (MCI_ERROR_NO_ERROR != MCIDestroyCompression(pfol->mch)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + fOK = FALSE; + } + break; + +#ifndef BIT16 + case tcompTYPE_QUANTUM: + if (MCI_ERROR_NO_ERROR != QCIDestroyCompression(pfol->mch)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + fOK = FALSE; + } + break; +#endif + + default: + ErfSetCodes(pfol->perf,FCIERR_BAD_COMPR_TYPE,0); + } + + //** Now free the buffers + if (pfol->pchCompr) { + (*pfol->pfnfree)(pfol->pchCompr); + pfol->pchCompr = NULL; // Simplify error cleanup + } + + if (pfol->pchUncompr) { + (*pfol->pfnfree)(pfol->pchUncompr); + pfol->pchUncompr = NULL; // Simplify error cleanup + } + + return fOK; +} + + +/*** MCICreateCompressionGlobal -- Create the currently selected compressor + * + * Entry: + * pfol - Pointer to folder context + * + * Exit-Success: + * Returns TRUE + * + * Exit-Failure: + * Returns FALSE, error structure filled in + */ +BOOL MCICreateCompressionGlobal(PFOL pfol) +{ +#ifndef BIT16 + QUANTUMCONFIGURATION qcfg; +#endif + + pfol->cbSrcBuffer = CB_MAX_CHUNK; + pfol->pchCompr = NULL; // Simplify error cleanup + pfol->pchUncompr = NULL; // Simplify error cleanup + + switch(CompressionTypeFromTCOMP(pfol->typeCompress)) { + case tcompBAD: // no new compressor + return TRUE; // all done if no compressor enabled + + case tcompTYPE_NONE: + pfol->cbDstBuffer = pfol->cbSrcBuffer; + break; + + case tcompTYPE_MSZIP: + if (MCI_ERROR_NO_ERROR != MCICreateCompression(&pfol->cbSrcBuffer, + pfol->pfnalloc, + pfol->pfnfree, + &pfol->cbDstBuffer, + &pfol->mch)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + return FALSE; + } + break; + +#ifndef BIT16 + case tcompTYPE_QUANTUM: + qcfg.CompressionLevel = CompressionLevelFromTCOMP(pfol->typeCompress); + qcfg.WindowBits = CompressionMemoryFromTCOMP(pfol->typeCompress); +//BUGBUG 01-Jun-1994 bens What is 3rd member of QCICreate structure + qcfg.HackHack = -1; // Why? + if (MCI_ERROR_NO_ERROR != QCICreateCompression(&pfol->cbSrcBuffer, + &qcfg, + pfol->pfnalloc, + pfol->pfnfree, + &pfol->cbDstBuffer, + &pfol->mch)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + return FALSE; + } + break; +#endif + + default: + ErfSetCodes(pfol->perf,FCIERR_BAD_COMPR_TYPE,0); + return FALSE; + } + + //** Now allocate whatever buffers the selected compressor requested + if (NULL == (pfol->pchCompr=(char *)pfol->pfnalloc(pfol->cbDstBuffer))) { + ErfSetCodes(pfol->perf,FCIERR_ALLOC_FAIL,0); + goto error; + } + + if (NULL == (pfol->pchUncompr=(char *)pfol->pfnalloc(pfol->cbSrcBuffer))) { + ErfSetCodes(pfol->perf,FCIERR_ALLOC_FAIL,0); + goto error; + } + + return TRUE; + +error: + MCIDestroyCompressionGlobal(pfol); // Clean up + return FALSE; // Failure +} + + +/*** MCIResetCompressionGlobal -- reset the currently selected compressor + * + * Entry: + * pfol -- pointer to folder context + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error structure filled in + */ +BOOL MCIResetCompressionGlobal(PFOL pfol) +{ + switch(CompressionTypeFromTCOMP(pfol->typeCompress)) { + case tcompBAD: + case tcompTYPE_NONE: + break; // no action for null compressor or none installed + + case tcompTYPE_MSZIP: + if (MCI_ERROR_NO_ERROR != MCIResetCompression(pfol->mch)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + return FALSE; + } + break; + +#ifndef BIT16 + case tcompTYPE_QUANTUM: + if (MCI_ERROR_NO_ERROR != QCIResetCompression(pfol->mch)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + return FALSE; + } + break; +#endif + + default: + ErfSetCodes(pfol->perf,FCIERR_BAD_COMPR_TYPE,0); + return FALSE; + } + return TRUE; +} + + +/*** MCICompressGlobal -- Compress with the currently selected compressor + * + * Entry: + * pfol - Pointer to folder context + * cbData - Location to return the compressed size + * + * Exit-Success: + * Returns TRUE + * + * Exit-Failure: + * Returns FALSE, error filled in + */ +BOOL MCICompressGlobal(PFOL pfol,USHORT *pcbData) +{ + UINT cbData; + + switch(CompressionTypeFromTCOMP(pfol->typeCompress)) { + case tcompTYPE_NONE: + memcpy(pfol->pchCompr,pfol->pchUncompr,*pcbData=pfol->cbBuffered); + break; + + case tcompTYPE_MSZIP: + if (MCI_ERROR_NO_ERROR != MCICompress(pfol->mch, + pfol->pchUncompr, + pfol->cbBuffered, + pfol->pchCompr, + pfol->cbDstBuffer, + &cbData)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + return FALSE; + } + //** Step down + *pcbData = (USHORT)cbData; + break; + +#ifndef BIT16 + case tcompTYPE_QUANTUM: + if (MCI_ERROR_NO_ERROR != QCICompress(pfol->mch, + pfol->pchUncompr, + pfol->cbBuffered, + pfol->pchCompr, + pfol->cbDstBuffer, + &cbData)) { + ErfSetCodes(pfol->perf,FCIERR_MCI_FAIL,0); + return FALSE; + } + //** Step down + *pcbData = (USHORT)cbData; + break; +#endif + + default: + ErfSetCodes(pfol->perf,FCIERR_BAD_COMPR_TYPE,0); + return FALSE; // Fatal error -- no valid compressor initialized + } + +#ifndef REMOVE_CHICAGO_M6_HACK +//BUGBUG 18-May-1994 bens Hack for Chicago M6 +// AndyHi & Co. are very risk averse, and so do not want to pick up a +// version of FDI.LIB newer than 302. Unfortunately, 302 has a bug +// in both FCI.LIB and FDI.LIB that cause diamond.exe to Assert() when +// an incompressible block is generated, and cause extract.exe (fdi.lib, +// really) to detect corrupted compressed data when an incompressible +// block is encountered. +// +// So, FCI has a temporary option to detect and fail when incompressible +// data is encountered, and DIAMOND.EXE has a switch to turn on this +// checking! Once Chicago M6 is released, we can destroy all this code! +// + //** Check for incompressible data and fail if told to do so; + if ((pfol->fFailOnIncompressible) && // Fail if incompressible + (cbData > pfol->cbSrcBuffer)) { // Output bigger than source buffer + ErfSetCodes(pfol->perf,FCIERR_M6_HACK_INCOMPRESSIBLE,0); + return FALSE; // Fatal error -- no valid compressor initialized + } +#endif + + return TRUE; +} diff --git a/private/windows/diamond/chuck/cabinet.h b/private/windows/diamond/chuck/cabinet.h new file mode 100644 index 000000000..39fe0caa0 --- /dev/null +++ b/private/windows/diamond/chuck/cabinet.h @@ -0,0 +1,380 @@ +/*** cabinet.h - Definitions for Cabinet File structure + * + * Author: + * Benjamin W. Slivka + * + * History: + * 15-Aug-1993 bens Initial version + * 05-Sep-1993 bens Added Overview section + * 29-Nov-1993 chuckst Added disk names to folder first & next + * Used "CF" consistently + * Eliminated redundant cch fields + * 09-Feb-1994 chuckst merged in some related global constants + * 09-Mar-1994 bens Add RESERVE defintions (for encryption) + * 17-Mar-1994 bens Improve comments about split CFDATA structures + * 25-Mar-1994 bens Add cabinet set ID + * 13-May-1994 bens Define bad value for iCabinet + * + * Overview: + * This file contains definitions for the Diamond Cabinet File format. + * A Cabinet File exists to store one or more files. Usually these + * files have been compressed, but that is not required. It is also + * possible for a cabinet file to contain only a portion of a larger + * file. + * + * In designing this format, the following goals where achieved: + * 1) Minimize overhead in the CF format + * ==> Where ever possible BYTEs or USHORTs were used, rather + * than using LONGs, even though the latter would be easier + * to manipulate on certain RISC platforms. + * 2) Support little-endian and big-endian byte ordering. + * ==> For simplicity on x86 systems, multi-byte numbers are + * stored in a little-endian form, but the code to read and + * write these numbers operates correctly on either type of + * computer. + * + * A cabinet file contains the following structures in the following + * order: + * Name Description + * ----------- ------------------- + * CFHEADER Cabinet description + * [CFRESERVE] Optional RESERVED control information in CFHEADER + * CFFOLDER(s) Folder descriptions + * [reserved] Optional RESERVED data per folder + * CFFILE(s) File descriptions + * CFDATA(s) Data blocks + * [reserved] Optional RESERVED data per data block + * + * Data Integrity Strategy: + * The Cabinet File has built-in data integrity checks, since it is + * possible for customers to have damaged diskettes, or for accidental + * or malicious damage to occur. Rather than doing an individual + * checksum for the entire cabinet file (which would have a dramatic + * impact on the speed of installation from floppy disk, since the + * entire file would need to be read), we have per-component + * checksums, and compute and check them as we read the various + * components of the file. + * + * 1) Checksum CFHEADER + * 2) Store cabinet file length in CFHEADER (to detect file truncation) + * 3) Checksum entire set of CFFOLDER structures + * 4) Checksum entire set of CFFILE structures + * 5) Checksum each (compressed) data block independantly + * + * This approach allows us to avoid reading unnecessary parts of the + * file cabinet (though reading all of CFFOLDER and CFFILE structures + * would otherwise not be required in all cases), while still providing + * adequate integrity checking. + */ + +#ifndef INCLUDED_CABINET +#define INCLUDED_CABINET 1 + +//** Pack structures tightly in cabinet files! +#pragma pack(1) + + +/*** verCF - Cabinet File format version + * + * The low-order byte is interpreted as a decimal number for the minor + * (1/100ths) portion of the version number. + * The high-order byte is interpreted as a decimal number for the major + * portion of the version number. + * + * Examples: + * 0x0000 0.00 + * 0x010A 1.10 + * 0x0410 4.16 + * + * History: + * 1.01 Original + * 1.02 Added flags field, changed signature + * 1.03 Added setId,iCabinet so FDI can ensure correct cabinet + * on continuation. + */ +#define verCF 0x0103 // CF version 1.03 + + +/*** Various cabinet file limits + * + */ +#define cMAX_FOLDERS_PER_CABINET (ifoldMASK-1) +#define cMAX_FILES_PER_CABINET 65535 + + +/*** cbRESERVE_XXX_MAX - Maximum size of RESERVE sections + * + * NOTE: cbRESERVE_HEADER_MAX is a fair bit less than 64K because in + * the 16-bit version of this code, we want to have a USHORT + * variable that holds the size of the CFHEADER structure + + * the size of the CFRESERVE structure + the size of the per-header + * reserved data. + */ +//BUGBUG 16-Mar-1994 bens Define better bound for cbRESERVE_HEADER_MAX +#define cbRESERVE_HEADER_MAX 60000 // Fits in a USHORT +#define cbRESERVE_FOLDER_MAX 255 // Fits in a BYTE +#define cbRESERVE_DATA_MAX 255 // Fits in a BYTE + + +/*** ifoldXXXX - Special values for CFFILE.iFolder + * + */ +#define ifoldMASK 0xFFFC // Low two bits zero +#define ifoldCONTINUED_FROM_PREV 0xFFFD +#define ifoldCONTINUED_TO_NEXT 0xFFFE +#define ifoldCONTINUED_PREV_AND_NEXT 0xFFFF + +#define IS_CONTD_FORWARD(ifold) ((ifold & 0xfffe) == ifoldCONTINUED_TO_NEXT) +#define IS_CONTD_BACK(ifold) ((ifold & 0xfffd) == ifoldCONTINUED_FROM_PREV) + + +#ifndef MAKESIG +/*** MAKESIG - Construct a 4 byte signature + * + * Entry: + * ch1,ch2,ch3,ch4 - four characters + * + * Exit: + * returns unsigned long + */ +#define MAKESIG(ch1,ch2,ch3,ch4) \ + ( ((unsigned long)ch1) + \ + (((unsigned long)ch2)<< 8) + \ + (((unsigned long)ch3)<<16) + \ + (((unsigned long)ch4)<<24) ) +#endif // !MAKESIG + + +#define sigCFHEADER MAKESIG('M','S','C','F') // CFHEADER signature + + +/*** cfhdrXXX - bit flags for cfheader.flags field + * + */ +#define cfhdrPREV_CABINET 0x0001 // Set if previous cab/disk present +#define cfhdrNEXT_CABINET 0x0002 // Set if next cab/disk present +#define cfhdrRESERVE_PRESENT 0x0004 // Set if RESERVE_CONTROL is present + + +/*** CFHEADER - Cabinet File Header + * + */ +typedef struct { +//** LONGs are first, to ensure alignment + long sig; // Cabinet File identification string + CHECKSUM csumHeader; // Structure checksum (excluding csumHeader!) + long cbCabinet; // Total length of file (consistency check) + CHECKSUM csumFolders; // Checksum of CFFOLDER list + COFF coffFiles; // Location in cabinet file of CFFILE list + CHECKSUM csumFiles; // Checksum of CFFILE list + +//** SHORTs are next, to ensure alignment + USHORT version; // Cabinet File version (verCF) + USHORT cFolders; // Count of folders (CFIFOLDERs) in cabinet + USHORT cFiles; // Count of files (CFIFILEs) in cabinet + USHORT flags; // Flags to indicate optional data presence + USHORT setID; // Cabinet set ID (identifies set of cabinets) + USHORT iCabinet; // Cabinet number in set (0 based) +#define iCABINET_BAD 0xFFFF // Illegal number for iCabinet + +//** If flags has the cfhdrRESERVE_PRESENT bit set, then a CFRESERVE +// structure appears here, followed possibly by some CFHEADER reserved +// space. The CFRESERVE structure has fields to define how much reserved +// space is present in the CFHEADER, CFFOLDER, and CFDATA structures. +// If CFRESERVE.cbCFHeader is non-zero, then abReserve[] immediately +// follows the CFRESERVE structure. Note that all of these sizes are +// multiples of 4 bytes, to ensure structure alignment! +// +// CFRESERVE cfres; // Reserve information +// BYTE abReserve[]; // Reserved data space +// + +//** The following fields presence depends upon the settings in the flags +// field above. If cfhdrPREV_CABINET is set, then there are two ASCIIZ +// strings to describe the previous disk and cabinet. +// +// NOTE: This "previous" cabinet is not necessarily the immediately +// preceding cabinet! While it usually will be, if a file is +// continued into the current cabinet, then the "previous" +// cabinet identifies the cabinet where the folder that contains +// this file *starts*! For example, if EXCEL.EXE starts in +// cabinet excel.1 and is continued through excel.2 to excel.3, +// then cabinet excel.3 will point back to *cabinet.1*, since +// that is where you have to start in order to extract EXCEL.EXE. +// +// char szCabinetPrev[]; // Prev Cabinet filespec +// char szDiskPrev[]; // Prev descriptive disk name +// +// Similarly, If cfhdrNEXT_CABINET is set, then there are two ASCIIZ +// strings to describe the next disk and cabinet: +// +// char szCabinetNext[]; // Next Cabinet filespec +// char szDiskNext[]; // Next descriptive disk name +// +} CFHEADER; /* cfheader */ + + +/*** CFRESERVE - Cabinet File Reserved data information + * + * This structure is present in the middle of the CFHEADER structure if + * CFHEADER.flags has the cfhdrRESERVE_PRESENT bit set. This structure + * defines the sizes of all the reserved data sections in the CFHEADER, + * CFFOLDER, and CFDATA structures. + * + * These reserved sizes can be zero (although it would be silly to have + * all of them be zero), but otherwise must be a multiple of 4, to ensure + * structure alignment for RISC machines. + */ +typedef struct { + USHORT cbCFHeader; // Size of abReserve in CFHEADER structure + BYTE cbCFFolder; // Size of abReserve in CFFOLDER structure + BYTE cbCFData; // Size of abReserve in CFDATA structure +} CFRESERVE; /* cfreserve */ + +#define cbCF_HEADER_BAD 0xFFFF // Bad value for CFRESERVE.cbCFHeader + + +/*** CFFOLDER - Cabinet Folder + * + * This structure describes a partial or complete "compression unit". + * A folder is by definition a stream of compressed data. To retrieve + * an uncompressed data from a folder, you *must* start decompressing + * the data at the start of the folder, regardless of how far into the + * folder the data you want actually starts. + * + * Folders may start in one cabinet, and continue on to one or more + * succeeding cabinets. In general, if a folder has been continued over + * a cabinet boundary, Diamond/FCI will terminate that folder as soon as + * the current file has been completely compressed. Generally this means + * that a folder would span at most two cabinets, but if the file is really + * large, it could span more than two cabinets. + * + * Note: CFFOLDERs actually refer to folder *fragments*, not necessarily + * complete folders. You know that a CFFOLDER is the beginning of a + * folder (as opposed to a continuation in a subsequent cabinet file) + * if a file starts in it (i.e., the CFFILE.uoffFolderStart field is + * 0). + */ +typedef struct { + COFF coffCabStart; // Offset in cabinet file of first CFDATA + // block for this folder. + + USHORT cCFData; // Count of CFDATAs for this folder that + // are actually in this cabinet. Note that + // a folder can continue into another cabinet + // and have many more CFDATA blocks in that + // cabinet, *and* a folder may have started + // in a previous cabinet. This count is + // only of CFDATAs for this folder that are + // (at least partially) in this cabinet. + + short typeCompress; // Indicates compression type for all CFDATA + // blocks for this folder. The valid values + // are defined in the types.h built into + // fci.h/fdi.h. + +//** If CFHEADER.flags has the cfhdrRESERVE_PRESENT bit set, and +// CFRESERVE.cbCFFolder is non-zero, then abReserve[] appears here. +// +// BYTE abReserve[]; // Reserved data space +// +} CFFOLDER; /* cffolder */ + + + +/*** CFFILE - Cabinet File structure describing a single file in the cabinet + * + * NOTE: iFolder is used to indicatate continuation cases, so we have to + * calculate the real iFolder by examining the cabinet files: + * + * ifoldCONTINUED_FROM_PREV + * This file ends in this cabinet, but is continued from a + * previous cabinet. Therefore, the portion of the file contained + * in *this* cabinet *must* start in the first folder. + * + * NOTE: szCabinetPrev is the name of the cabinet where this file + * *starts*, which is not necessarily the immediately + * preceeding cabinet! Since it only makes sense to + * decompress a file from its start, the starting cabinet + * is what is important! + * + * ifoldCONTINUED_TO_NEXT + * This file starts in this cabinet, but is continued to the next + * cabinet. Therfore, this file must start in the *last* folder + * in this cabinet. + * + * ifoldCONTINUED_PREV_AND_NEXT + * This file is the *middle* portion of a file that started in a + * previous cabinet and is continued in the next cabinet. Since + * this cabinet only contain this piece of a single file, there + * is only a single folder fragment in this cabinet. + */ +typedef struct { + long cbFile; // Uncompressed size of file + + UOFF uoffFolderStart; // Offset in folder IN UNCOMPRESSED BYTES + // of the start of this file + + USHORT iFolder; // Index of folder containing this file; + // 0 is first folder in this cabinet. + // See ifoldCONTINUED_XXXX values above + // for treatment of continuation files. + + USHORT date; // Date stamp in FAT file system format + + USHORT time; // Time stamp in FAT file system format + + USHORT attribs; // Attribute in FAT file system format + +// char szName[]; // File name (may include path characters) +} CFFILE; /* cffile */ + + +/*** CFDATA - Cabinet File structure describing a data block + * + */ +typedef struct { + CHECKSUM csum; // Checksum (excluding this field itself!) + // of this structure and the data that + // follows. If this CFDATA structure is + // continued to the next cabinet, then + // the value of this field is ignored + // (and set to zero). + + USHORT cbData; // Size of ab[] data that resides in the + // current cabinet. A CFDATA may be split + // across a cabinet boundary, so this + // value indicates only the amount of data + // store in this cabinet. + + USHORT cbUncomp; // Uncompressed size of ab[] data; if this + // CFDATA block is continued to the next + // cabinet, then this value is zero! + // If this CFDATA block the remainder of + // of a CFDATA block that started in the + // previous cabinet, then this value is + // the total size of the uncompressed data + // represented by the two CFDATA blocks! + +//** If CFHEADER.flags has the cfhdrRESERVE_PRESENT bit set, and +// CFRESERVE.cbCFData is non-zero, then abReserve[] appears here. +// +// BYTE abReserve[]; // Reserved data space +// + +//** The actual data follows here, cbData bytes in length. +// +// BYTE ab[]; // Data +// +} CFDATA; /* cfdata */ + + + +//** Attribute Bit to use for Run after extract +#define RUNATTRIB 0x40 + + +//** Revert to default structure packing! +#pragma pack() + +#endif // !INCLUDED_CABINET diff --git a/private/windows/diamond/chuck/checksum.c b/private/windows/diamond/chuck/checksum.c new file mode 100644 index 000000000..75d6032e5 --- /dev/null +++ b/private/windows/diamond/chuck/checksum.c @@ -0,0 +1,61 @@ +/*** checksum.c - Checksum Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 15-Aug-1993 bens Initial version + * 17-Mar-1994 bens Cleaned up code (need better checksum, still!) + */ + +#include "types.h" +#include "checksum.h" + + +/*** CSUMCompute - Compute checksum of a data block + * + * NOTE: See checksum.h for entry/exit conditions. + */ +CHECKSUM CSUMCompute(void *pv, UINT cb, CHECKSUM seed) +{ + int cUlong; // Number of ULONGs in block + CHECKSUM csum; // Checksum accumulator + BYTE *pb; + ULONG ul; + + cUlong = cb / 4; // Number of ULONGs + csum = seed; // Init checksum + pb = pv; // Start at front of data block + + //** Checksum integral multiple of ULONGs + while (cUlong-- > 0) { + //** NOTE: Build ULONG in big/little-endian independent manner + ul = *pb++; // Get low-order byte + ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte + ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte + ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte + + csum ^= ul; // Update checksum + } + + //** Checksum remainder bytes + ul = 0; + switch (cb % 4) { + case 3: + ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte + case 2: + ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte + case 1: + ul |= *pb++; // Get low-order byte + default: + break; + } + csum ^= ul; // Update checksum + + //** Return computed checksum + return csum; +} diff --git a/private/windows/diamond/chuck/checksum.h b/private/windows/diamond/chuck/checksum.h new file mode 100644 index 000000000..914defb9e --- /dev/null +++ b/private/windows/diamond/chuck/checksum.h @@ -0,0 +1,25 @@ +/*** checksum.h - Definitions for Checksum Manager + * + * Author: + * Benjamin W. Slivka + * + * History: + * 15-Aug-1993 bens Initial version. + */ + +#ifndef INCLUDED_CHECKSUM +#define INCLUDED_CHECKSUM 1 + + +/*** CSUMCompute - Compute checksum of a data block + * + * Entry: + * pv - block to checksum + * cb - length of block (in bytes) + * seed - previous checksum + * + * Exit-Success: + * returns CHECKSUM for block + */ +CHECKSUM CSUMCompute(void *pv, UINT cb, CHECKSUM seed); +#endif // !INCLUDED_CHECKSUM diff --git a/private/windows/diamond/chuck/dirs b/private/windows/diamond/chuck/dirs new file mode 100644 index 000000000..4bd04dab6 --- /dev/null +++ b/private/windows/diamond/chuck/dirs @@ -0,0 +1,2 @@ +DIRS=fci.nt fdi.nt +
\ No newline at end of file diff --git a/private/windows/diamond/chuck/erf.c b/private/windows/diamond/chuck/erf.c new file mode 100644 index 000000000..f5e155755 --- /dev/null +++ b/private/windows/diamond/chuck/erf.c @@ -0,0 +1,25 @@ +/*** erf.c - Error Reporting + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Feb-1994 bens Remove all message-related stuff + */ + +#include "types.h" +#include "asrt.h" +#include "erf.h" + + +/*** ErfSetCodes - Set error codes + * + * NOTE: See erf.h for entry/exit conditions. + */ +void ErfSetCodes(PERF perf, int erfOper, int erfType) +{ + + Assert(perf!=NULL); + + perf->erfOper = erfOper; + perf->erfType = erfType; + perf->fError = TRUE; +} diff --git a/private/windows/diamond/chuck/erf.h b/private/windows/diamond/chuck/erf.h new file mode 100644 index 000000000..b39871105 --- /dev/null +++ b/private/windows/diamond/chuck/erf.h @@ -0,0 +1,27 @@ +/*** erf.h - Definitions for Error Reporting + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Feb-1994 bens Remove all message-related stuff + */ + +#ifndef INCLUDED_FCI_ERF +#define INCLUDED_FCI_ERF 1 + + +/*** ErfSetCodes - Set error codes (no message formatting) + * + * Entry + * perf - ERF structure to receive formatted message + * erfOper - Internal error code + * erfType - errno value (usually) + * + * Exit-Success + * perf - is filled in with appropriate error fields + * + * Exit-Failure + * perf filled in with message describing bad arguments. + */ +void ErfSetCodes(PERF perf, int erfOper, int erfType); + +#endif // !INCLUDED_FCI_ERF diff --git a/private/windows/diamond/chuck/fci.c b/private/windows/diamond/chuck/fci.c new file mode 100644 index 000000000..6c8e8236a --- /dev/null +++ b/private/windows/diamond/chuck/fci.c @@ -0,0 +1,319 @@ +/*** fci.c - Diamond File Compression Interface library + * + * Author: + * Chuck Strouss + * + * History: + * 21-Jan-1994 chuckst basic FCI structure in place + * 09-Mar-1994 bens Add RESERVE support + * 21-Mar-1994 bens Use spruced up fci_int.h definitions + * 26-May-1994 bens Add Quantum support + */ + +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys\stat.h> +#include <io.h> +#include <errno.h> + +#include "types.h" +#include "asrt.h" +#include "fci_int.h" +#include "cabinet.h" +#include "erf.h" +#include "..\mszip\mci.h" +#include "buildcab.h" +#include "checksum.h" + +/*** FCI - FCI context structure + * + */ +typedef struct { /* fci */ +#ifdef ASSERT + SIGNATURE sig; +#endif + PCAB pcab; + PFOL pfol; + PERF perf; + PFNFREE pfnfree; // memory free function + void *pv; // used for passing back to callbacks +} FCI; + +typedef FCI *PFCI; /* pfci */ + +#define PFCIfromHFCI(hfci) ((PFCI)(hfci)) +#define HFCIfromPFCI(pfci) ((HFCI)(pfci)) + + +#ifdef ASSERT +#define sigFCI MAKESIG('F','C','I','X') // FCI signature +#define AssertFCI(pfci) AssertStructure(pfci,sigFCI); +#else // !ASSERT +#define AssertFCI(pfci) +#endif // !ASSERT + + +/*** FCICreate -- create an FCI context (an open CAB, an open FOL) + * + * NOTE: See fci_int.h for entry/exit conditions. + */ +HFCI DIAMONDAPI FCICreate(PERF perf, + PFNFCIFILEPLACED pfnfiledest, + PFNALLOC pfnalloc, + PFNFREE pfnfree, + PFNFCIGETTEMPFILE pfntemp, + PCCAB pccab) +{ + PFCI pfci; + + pfci = (PFCI) pfnalloc (sizeof (FCI)); + if (pfci == NULL) { + ErfSetCodes(perf,FCIERR_ALLOC_FAIL,0); + return NULL; + } + SetAssertSignature(pfci,sigFCI); + + AssertFCI(pfci); // Make sure we've really set sig + pfci->pfnfree = pfnfree; // Save free function in our context + pfci->pcab = NULL; // assume cabinet not initialized yet for err exit + + pfci->pfol = FolderInit(perf, + pfnalloc, + pfnfree, + pfntemp, + sizeof(CFDATA) + pccab->cbReserveCFData); + if (pfci->pfol == NULL) { + goto error; + } +#ifndef REMOVE_CHICAGO_M6_HACK + //** Save incompressible failure flag in folder, so we can test + // it after each block we attempt to compress. + pfci->pfol->fFailOnIncompressible = pccab->fFailOnIncompressible; +#endif + + pfci->pcab = CreateCab("tell.cab", + perf, + pfnfiledest, + pfnalloc, + pfnfree, + pfntemp); + if (pfci->pcab == NULL) { + goto error; // error already filled in + } + pfci->pcab->ccab = *pccab; // save cabinet name structure + pfci->pcab->setID = pccab->setID; // Save cabinet set ID + pfci->pcab->iCabinet = 0; // Cabinet number starts at 0 + + //** Check for RESERVEd data +//BUGBUG 15-Mar-1994 bens Should we return an error if too big? + Assert(pccab->cbReserveCFHeader <= cbRESERVE_HEADER_MAX); + Assert(pccab->cbReserveCFFolder <= cbRESERVE_FOLDER_MAX); + Assert(pccab->cbReserveCFData <= cbRESERVE_DATA_MAX); + if ((pccab->cbReserveCFHeader > 0) || // If any reserved data + (pccab->cbReserveCFFolder > 0) || + (pccab->cbReserveCFData > 0)) { + //** Need a CFRESERVE structure in the CFHEADER + pfci->pcab->cbReserveCF = sizeof(CFRESERVE) + + pccab->cbReserveCFHeader; + } + else { + pfci->pcab->cbReserveCF = 0; // No reserved areas + } + + //** Compute size of structures with reserved areas + pfci->pcab->cbCFHeaderPlusReserve = sizeof(CFHEADER) + + pfci->pcab->cbReserveCF; + + pfci->pcab->cbCFFolderPlusReserve = sizeof(CFFOLDER) + + pccab->cbReserveCFFolder; + + pfci->pcab->cbCFDataPlusReserve = sizeof(CFDATA) + + pccab->cbReserveCFData; + + //** Check for defaults + if (pfci->pcab->ccab.cb == 0) { // 0 implies MAX + pfci->pcab->ccab.cb = CB_MAX_DISK; + } + + return HFCIfromPFCI(pfci); // return our handle! + +error: + FCIDestroy(pfci); + return NULL; +} + + +/*** FCIAddFile - Add file to cabinet + * + * NOTE: See fci_int.h for entry/exit conditions. + */ +BOOL DIAMONDAPI FCIAddFile(HFCI hfci, + char *pszSourceFile, + char *pszFileName, + BOOL fExecute, + PFNFCIGETNEXTCABINET GetNextCab, + PFNFCISTATUS pfnProgress, + PFNFCIGETOPENINFO pfnOpenInfo, + TCOMP typeCompress, + void *pv) +{ + PFCI pfci; + pfci = PFCIfromHFCI(hfci); + AssertFCI(pfci); + AssertCAB(pfci->pcab); + AssertFOL(pfci->pfol); + +#ifndef REMOVE_CHICAGO_M6_HACK + //** Make sure state is passing around correctly + Assert(pfci->pfol->fFailOnIncompressible == pfci->pcab->ccab.fFailOnIncompressible); +#endif + + //** Make sure the compressor is initialized to the type of compression + // the caller has requested. + if (typeCompress != pfci->pfol->typeCompress) + { + //** First terminate any pending compression stuff + if (!AddFolderToCabinet(pfci->pcab, + pfci->pfol, + FALSE, // Do not force GetNextCabinet + GetNextCab, + pfnProgress, + pv)) { + return FALSE; + } + if (!SetCompressionType(typeCompress,pfci->pfol)) { + return FALSE; + } + } + +#ifndef REMOVE_CHICAGO_M6_HACK + //** Make sure state is passing around correctly + Assert(pfci->pfol->fFailOnIncompressible == pfci->pcab->ccab.fFailOnIncompressible); +#endif + + //** Add the file to the current folder + if (!AddFileToFolder(pfci->pfol, + pszSourceFile, // filename to read from + pszFileName, // name it will have in folder + fExecute, + pfnProgress, + pfnOpenInfo, // callback for opening file, etc. + typeCompress, + pv)) { + return FALSE; + } + + //** See if folder being built exceeds folder threshold, or exceeds + // maximum specified cabinet size + if ((pfci->pcab->ccab.cbFolderThresh < pfci->pfol->tf[iftfCFDATA].cb) || + (pfci->pcab->ccab.cb < + (pfci->pcab->cbCFHeaderPlusReserve + // still need CFHEADER + pfci->pcab->cbCFFolderPlusReserve + // and one more CFFOLDER + pfci->pcab->tf[ictfCFDATA].cb + + pfci->pcab->tf[ictfCFFILE].cb + + pfci->pcab->tf[ictfCFFOLDER].cb + + pfci->pfol->tf[iftfCFDATA].cb + + pfci->pfol->tf[iftfCFFOLDER].cb))) { + + //** Need to add folder to cabinet + return AddFolderToCabinet(pfci->pcab, + pfci->pfol, + FALSE, // Do not force GetNextCabinet + GetNextCab, + pfnProgress, + pv); + } + + //** Success + return TRUE; +} + + +/*** FCIFlushCabinet - Complete the current cabinet under construction + * + * NOTE: See fci_int.h for entry/exit conditions. + */ +BOOL DIAMONDAPI FCIFlushCabinet(HFCI hfci, + BOOL fGetNextCab, + PFNFCIGETNEXTCABINET GetNextCab, + PFNFCISTATUS pfnProgress, + void *pv) +{ + PFCI pfci; + unsigned long cbDone; + unsigned long cbTot; + + pfci = PFCIfromHFCI(hfci); + AssertFCI(pfci); + + if (!AddFolderToCabinet(pfci->pcab, + pfci->pfol, + fGetNextCab, + GetNextCab, + pfnProgress, + pv)) { + return FALSE; // error already filled in + } + + //** If fGetNextCab is TRUE, then AddFolderToCabinet did the FlushCab, + // so don't do it again here! + if (fGetNextCab) { + return TRUE; + } + + //** Need to flush the remainder of the cabinet + cbDone = 0; + cbTot = pfci->pcab->tf[ictfCFFILE].cb + + pfci->pcab->tf[ictfCFDATA].cb; + return FlushCab(pfci->pcab, + pfnProgress, + &cbDone, + cbTot, + pv); +} + + +/*** FCIFlushFolder - Complete the current folder under construction + * + * NOTE: See fci_int.h for entry/exit conditions. + */ +BOOL DIAMONDAPI FCIFlushFolder(HFCI hfci, + PFNFCIGETNEXTCABINET GetNextCab, + PFNFCISTATUS pfnProgress, + void *pv) +{ + PFCI pfci; + + pfci = PFCIfromHFCI(hfci); + AssertFCI(pfci); + + if (!AddFolderToCabinet(pfci->pcab, + pfci->pfol, + FALSE, // Do not force GetNextCabinet + GetNextCab, + pfnProgress, + pv)) { + return FALSE; // error already filled in + } + return TRUE; +} + + +/*** FCIDestroy -- Destroy an FCI context + * + * NOTE: See fci_int.h for entry/exit conditions. + */ +BOOL DIAMONDAPI FCIDestroy(HFCI hfci) +{ + PFCI pfci; + BOOL retcode = TRUE; + + pfci = PFCIfromHFCI(hfci); + AssertFCI(pfci); + if (pfci->pfol != NULL) retcode &= FolderDestroy(pfci->pfol); + if (pfci->pcab != NULL) retcode &= CabDestroy(pfci->pcab); + ClearAssertSignature(pfci); + pfci->pfnfree(pfci); + return retcode; +} diff --git a/private/windows/diamond/chuck/fci.nt/makefile b/private/windows/diamond/chuck/fci.nt/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/chuck/fci.nt/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/chuck/fci.nt/sources b/private/windows/diamond/chuck/fci.nt/sources new file mode 100644 index 000000000..5a49fc4f0 --- /dev/null +++ b/private/windows/diamond/chuck/fci.nt/sources @@ -0,0 +1,39 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Ted Miller (tedm) 19-Feb-1991 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=diamond +MINORCOMP=fci + +TARGETNAME=fci +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +SOURCES=..\fci.c \ + ..\checksum.c \ + ..\buildcab.c \ + ..\buildfol.c \ + ..\erf.c \ + ..\asrt.c + +UMTYPE=console diff --git a/private/windows/diamond/chuck/fci_int.h b/private/windows/diamond/chuck/fci_int.h new file mode 100644 index 000000000..89f580e30 --- /dev/null +++ b/private/windows/diamond/chuck/fci_int.h @@ -0,0 +1,390 @@ +/*** fci_int.h - File Compression Interface definitions + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Chuck Strouss + * + * History: + * 09-Jan-1994 chuckst Contents moved to bfol.h, this file is a + * placeholder for the new 'higher-level' fci + * 14-Feb-1994 bens Cleaned up some comments, added BUGBUGs. + * 09-Mar-1994 bens Added error codes (moved from buildcab.h); + * Added RESERVE control + * 17-Mar-1994 bens Specify structure packing explicitly + * 21-Mar-1994 bens Cleaned up names + * 22-Mar-1994 bens Documented error cods + * 29-Mar-1994 bens Add FCIFlushFolder, renamed FCIFlushCabinet + * 18-Apr-1994 bens Changed CDECL to DIAMONDAPI + * 18-May-1994 bens Add ccab.fFailOnIncompressible field for + * Chicago M6 hack. + */ + +#ifndef INCLUDED_FCI +#define INCLUDED_FCI 1 + +//** Specify structure packing explicitly for clients of FCI +#include <pshpack4.h> + + +/*** FCIERROR - Error codes returned in erf.erfOper field + * + */ +typedef enum { +FCIERR_NONE, // No error + +FCIERR_OPEN_SRC, // Failure opening file to be stored in cabinet + // erf.erfTyp has C run-time *errno* value + +FCIERR_READ_SRC, // Failure reading file to be stored in cabinet + // erf.erfTyp has C run-time *errno* value + +FCIERR_ALLOC_FAIL, // Out of memory in FCI + +FCIERR_TEMP_FILE, // Could not create a temporary file + // erf.erfTyp has C run-time *errno* value + +FCIERR_BAD_COMPR_TYPE, // Unknown compression type + +FCIERR_CAB_FILE, // Could not create cabinet file + // erf.erfTyp has C run-time *errno* value + +FCIERR_USER_ABORT, // Client requested abort + +FCIERR_MCI_FAIL, // Failure compressing data + +#ifndef REMOVE_CHICAGO_M6_HACK +FCIERR_M6_HACK_INCOMPRESSIBLE, // Data was incompressible +#endif +} FCIERROR; + + +/*** HFCI - Handle to an FCI Context + * + */ +typedef void * HFCI; + + +/*** CCAB - Current Cabinet + * + * This structure is used for passing in the cabinet parameters to FCI, + * and is passed back on certain FCI callbacks to provide cabinet + * information to the client. + */ +typedef struct { +// longs first + ULONG cb; // size available for cabinet on this media + ULONG cbFolderThresh; // Thresshold for forcing a new Folder + +// then ints + UINT cbReserveCFHeader; // Space to reserve in CFHEADER + UINT cbReserveCFFolder; // Space to reserve in CFFOLDER + UINT cbReserveCFData; // Space to reserve in CFDATA + int iCab; // sequential numbers for cabinets + int iDisk; // Disk number +#ifndef REMOVE_CHICAGO_M6_HACK + int fFailOnIncompressible; // TRUE => Fail if a block is incompressible +#endif + +// then shorts + USHORT setID; // Cabinet set ID + +// then chars + char szDisk[CB_MAX_DISK_NAME]; // current disk name + char szCab[CB_MAX_CABINET_NAME]; // current cabinet name + char szCabPath[CB_MAX_CAB_PATH]; // path for creating cabinet +} CCAB; /* ccab */ +typedef CCAB *PCCAB; /* pccab */ + + +/*** FNFCIGETNEXTCABINET - Callback used to request new cabinet info + * + * Entry: + * pccab - Points to copy of old ccab structure to modify + * cbPrevCab - Estimate of size of previous cabinet + * pv - Has the caller's context pointer + * + * Exit-Success: + * returns TRUE; + * + * Exit-Failure: + * returns FALSE; + */ +typedef BOOL (DIAMONDAPI *PFNFCIGETNEXTCABINET)(PCCAB pccab, + ULONG cbPrevCab, + void *pv); /* pfnfcignc */ + +#define FNFCIGETNEXTCABINET(fn) BOOL DIAMONDAPI fn(PCCAB pccab, \ + ULONG cbPrevCab, \ + void *pv) + + +/*** FNFCIFILEPLACED - Notify FCI client that file was placed + * + * Entry: + * pccab - cabinet structure to fill in, with copy of previous one + * pszFile - name of file, from cabinet + * cbFile - length of file + * fContinuation - true if this is a later segment of a continued file + * pv - the context of the client + * + * Exit-Success: + * return value anything but -1 + * + * Exit-Failure: + * return value -1 means to abort + */ +typedef int (DIAMONDAPI *PFNFCIFILEPLACED)(PCCAB pccab, + char *pszFile, + long cbFile, + BOOL fContinuation, + void *pv); /* pfnfcifp */ + +#define FNFCIFILEPLACED(fn) int DIAMONDAPI fn(PCCAB pccab, \ + char *pszFile, \ + long cbFile, \ + BOOL fContinuation, \ + void *pv) + + +/*** FNCDIGETOPENINFO - Open source file, get date/time/attribs + * + * Entry: + * pszName -- complete path to filename + * pdate -- location to return FAT-style date code + * ptime -- location to return FAT-style time code + * pattribs -- location to return FAT-style attributes + * pv -- client's context + * + * Exit-Success: + * Return value is file handle of open file to read + * + * Exit-Failure: + * Return value is -1 + */ +typedef int (DIAMONDAPI *PFNFCIGETOPENINFO)(char *pszName, + USHORT *pdate, + USHORT *ptime, + USHORT *pattribs, + void *pv); /* pfnfcigoi */ + +#define FNFCIGETOPENINFO(fn) int DIAMONDAPI fn(char *pszName, \ + USHORT *pdate, \ + USHORT *ptime, \ + USHORT *pattribs, \ + void *pv) + +/*** FNFCISTATUS - Status/Cabinet Size callback + * + * Entry: + * typeStatus == statusFile if compressing a block into a folder + * cb1 = Size of compressed block + * cb2 = Size of uncompressed block + * + * typeStatus == statusFolder if adding a folder to a cabinet + * cb1 = Amount of folder copied to cabinet so far + * cb2 = Total size of folder + * + * typeStatus == statusCabinet if writing out a complete cabinet + * cb1 = Estimated cabinet size that was previously + * passed to fnfciGetNextCabinet(). + * cb2 = Actual cabinet size + * NOTE: Return value is desired client size for cabinet + * file. FCI updates the maximum cabinet size + * remaining using this value. This allows a client + * to generate multiple cabinets per disk, and have + * FCI limit the size correctly -- the client can do + * cluster size rounding on the cabinet size! + * The client should either return cb2, or round cb2 + * up to some larger value and return that. + * Exit-Success: + * Returns anything other than -1; + * NOTE: See statusCabinet for special return values! + * + * Exit-Failure: + * Returns -1 to signal that FCI should abort; + */ + +#define statusFile 0 // Add File to Folder callback +#define statusFolder 1 // Add Folder to Cabinet callback +#define statusCabinet 2 // Write out a completed cabinet callback + +typedef long (DIAMONDAPI *PFNFCISTATUS)(UINT typeStatus, + ULONG cb1, + ULONG cb2, + void *pv); /* pfnfcis */ + +#define FNFCISTATUS(fn) long DIAMONDAPI fn(UINT typeStatus, \ + ULONG cb1, \ + ULONG cb2, \ + void *pv) + + +/*** FNFCIGETTEMPFILE - Callback, requests temporary file name + * + * Entry: + * pszTempName - Buffer to receive complete tempfile name + * cbTempName - Size of pszTempName buffer + * + * Exit-Success: + * return TRUE + * + * Exit-Failure: + * return FALSE; could not create tempfile, or buffer too small + * + * Note: + * It is conceivable that this function may return a filename + * that will already exist by the time it is opened. For this + * reason, the caller should make several attempts to create + * temporary files before giving up. + */ +typedef BOOL (DIAMONDAPI *PFNFCIGETTEMPFILE)(char *pszTempName, + int cbTempName); /* pfnfcigtf */ + +#define FNFCIGETTEMPFILE(fn) BOOL DIAMONDAPI fn(char *pszTempName, \ + int cbTempName) + + +/*** FCICreate -- create an FCI context (an open CAB, an open FOL) + * + * Entry: + * perf - structure where we return error codes + * pfnfcifp - callback to inform caller of eventual dest of files + * pfna - memory allocation function callback + * pfnf - memory free function callback + * pfnfcigtf - temp file name generator callback + * pccab - pointer to cabinet/disk name & size structure + * + * Notes: + * (1) The alloc/free callbacks must remain valid throughout + * the life of the context, up to and including the call to + * FCIDestroy. + * (2) The perf pointer is stored in the compression context (HCI), + * and any errors from subsequent FCI calls are stored in the + * erf that was passed in on *this* call. + * + * Exit-Success: + * Returns non-NULL handle to an FCI context. + * + * Exit-Failure: + * Returns NULL, perf filled in. + */ +HFCI DIAMONDAPI FCICreate(PERF perf, + PFNFCIFILEPLACED pfnfcifp, + PFNALLOC pfna, + PFNFREE pfnf, + PFNFCIGETTEMPFILE pfnfcigtf, + PCCAB pccab + ); + + +/*** FCIAddFile - Add a disk file to a folder/cabinet + * + * Entry: + * hfci - FCI context handle + * pszSourceFile - Name of file to add to folder + * pszFileName - Name to store into folder/cabinet + * fExecute - Flag indicating execute on extract + * pfn_progress - Progress callback + * pfnfcignc - GetNextCabinet callback + * pfnfcis - Status callback + * pfnfcigoi - OpenInfo callback + * typeCompress - Type of compression to use for this file + * pv - pointer to caller's internal context + * + * Exit-Success: + * returns TRUE + * + * Exit-Failure: + * returns FALSE, error filled in + * + * This is the main function used to add file(s) to a cabinet + * or series of cabinets. If the current file causes the current + * folder/cabinet to overflow the disk image currently being built, + * the cabinet will be terminated, and a new cabinet/disk name will + * be prompted for via a callback. The pending folder will be trimmed + * of the data which has already been generated in the finished cabinet. + */ +BOOL DIAMONDAPI FCIAddFile(HFCI hfci, + char *pszSourceFile, + char *pszFileName, + BOOL fExecute, + PFNFCIGETNEXTCABINET pfnfcignc, + PFNFCISTATUS pfnfcis, + PFNFCIGETOPENINFO pfnfcigoi, + TCOMP typeCompress, + void *pv + ); + + +/*** FCIFlushCabinet - Complete the current cabinet under construction + * + * This will cause the current cabinet (assuming it is not empty) to + * be gathered together and written to disk. + * + * Entry: + * hfci - FCI context + * fGetNextCab - TRUE => Call GetNextCab to get continuation info; + * FALSE => Don't call GetNextCab unless this cabinet + * overflows. + * pfnfcignc - callback function to get continuation cabinets + * pfnfcis - callback function for progress reporting + * pv - caller's internal context for callbacks + * + * Exit-Success: + * return code TRUE + * + * Exit-Failure: + * return code FALSE, error structure filled in + */ +BOOL DIAMONDAPI FCIFlushCabinet(HFCI hfci, + BOOL fGetNextCab, + PFNFCIGETNEXTCABINET pfnfcignc, + PFNFCISTATUS pfnfcis, + void *pv + ); + + +/*** FCIFlushFolder - Complete the current folder under construction + * + * This will force the termination of the current folder, which may or + * may not cause one or more cabinet files to be completed. + * + * Entry: + * hfci - FCI context + * GetNextCab - callback function to get continuation cabinets + * pfnProgress - callback function for progress reporting + * pv - caller's internal context for callbacks + * + * Exit-Success: + * return code TRUE + * + * Exit-Failure: + * return code FALSE, error structure filled in + */ +BOOL DIAMONDAPI FCIFlushFolder(HFCI hfci, + PFNFCIGETNEXTCABINET pfnfcignc, + PFNFCISTATUS pfnfcis, + void *pv + ); + + +/*** FCIDestroy - Destroy a FCI context and delete temp files + * + * Entry: + * hfci - FCI context + * + * Exit-Success: + * return code TRUE + * + * Exit-Failure: + * return code FALSE, error structure filled in + */ +BOOL DIAMONDAPI FCIDestroy (HFCI hfci); + +//** Revert to default structure packing +#include <poppack.h> + +#endif // !INCLUDED_FCI diff --git a/private/windows/diamond/chuck/fdi.c b/private/windows/diamond/chuck/fdi.c new file mode 100644 index 000000000..00e828442 --- /dev/null +++ b/private/windows/diamond/chuck/fdi.c @@ -0,0 +1,1626 @@ +/*** fdi.c - Diamond File Decompression Interface + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Chuck Strouss + * + * History: + * 24-jan-1994 chuckst Initial version + * 16-Mar-1994 bens Add RESERVE support + * 17-Mar-1994 bens Fix bug in CFDATA splitting introduced by RESERVE + * 21-Mar-1994 bens Use spruced up fci_int.h definitions + * 25-Mar-1994 bens Add fdintCABINET_INFO notification; reduce stack + * 07-Apr-1994 bens Add Decryption interfaces + * 02-Jun-1994 bens Added Quantum compression support + * 19-Aug-1994 bens Add cpuType parameter to FDICreate(). + * 31-Jan-1994 msliger Make CreateDecompress alloc buffers before MDI/QDI + * 3-Apr-1995 jeffwe Added chaining info to FDICABINETINFO + * + * Overview: + * The File Decompression Interface is used to simplify the reading of + * Diamond cabinet files. A setup program will proceed in a manner very + * similar to the pseudo code below. An FDI context is created, the + * setup program calls FDICopy() for each cabinet to be processed. For + * each file in the cabinet, FDICopy() calls a notification callback + * routine, asking the setup program if the file should be copied. + * This call-back approach is great because it allows the cabinet file + * to be read and decompressed in an optimal manner. + */ + +#include <fcntl.h> +#include <sys\stat.h> +#include <io.h> +#include <stdio.h> +#include <string.h> +#include "types.h" +#include "asrt.h" +#include "fdi_int.h" +#include "cabinet.h" +#include "erf.h" +#include "..\mszip\mdi.h" +#include "..\quantum\qdi.h" +#include "checksum.h" + +typedef struct { +// place (long) stuff first + +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFDI +#endif + PERF perf; + + PFNFREE pfnfree; // FDI client callback functions + PFNALLOC pfnalloc; + PFNOPEN pfnopen; + PFNREAD pfnread; + PFNWRITE pfnwrite; + PFNCLOSE pfnclose; + PFNSEEK pfnseek; + int cpuType; // CPU type (see cpuXXX in fdi_int.h) + + PFNFDINOTIFY pfnfdin; // passed in on FDICopy call + PFNFDIDECRYPT pfnfdid; // passed in on FDICopy call + + COFF coffFolders; // Offset of CFFOLDER start + UOFF uoffFolder; // Uncompressed offset of current CFDATA + // in current folder; + + MDI_CONTEXT_HANDLE mdh; // decompress context handle + void *pvUser; // User's callback context + char *pchCompr; // pointer to compressed data buffer + char *pchUncompr; // pointer to uncompressed data buffer + + CFFOLDER *pcffolder; // Dynamic due to variable RESERVE size + CFDATA *pcfdata; // Dynamic due to variable RESERVE size + void *pbCFHeaderReserve; // Reserved data for CFHEADER + +// Next comes the nested structures. + + CFHEADER cfheader; + CFFILE cffile; + +// Now the ints + + int hfCabData; // file handle of cabinet for Data reads + int hfCabFiles; // file handle of cabinet for File reads + int hfNew; // file handle we're writing to + UINT iFolder; // current folder we're operating on + UINT cbMaxUncompr; // maximum uncompressed block size + UINT cbMaxCompr; // maximum compressed block size + int fInContin; // In continuation cabinet flag + + UINT cbCFHeaderReserve; // Size of CFHEADER RESERVE + UINT cbCFFolderPlusReserve; // Size of CFFOLDER + folder RESERVE + UINT cbCFDataPlusReserve; // Size of CFDATA + data RESERVE + +// Now the shorts + USHORT cFilesRemain; // count of CFFILE entries remaining + USHORT cFilesSkipped; // count of CONTD_FORWARD files skipped + USHORT cCFDataRemain; // Number of CFDATA blocks left in folder + TCOMP typeCompress; // selected compression type + +// Now the piggy buffers + char achName [CB_MAX_FILENAME +1]; + + char achCabinetFirst[CB_MAX_CABINET_NAME+1]; + char achDiskFirst [CB_MAX_DISK_NAME +1]; + char achCabinetNext [CB_MAX_CABINET_NAME+1]; + char achDiskNext [CB_MAX_DISK_NAME +1]; + + char szCabPath [CB_MAX_CAB_PATH +1]; + +//NOTE: The following items could have been on the stack, but +// some FDI clients (NT setup, for example), are very tight on +// stack space, so we use our FDI context instead! + char szCabName [CB_MAX_CAB_PATH +1]; + FDINOTIFICATION fdin; // Notifcation structure + FDIDECRYPT fdid; // Decryption structure + +} FDI; /* fdi */ +typedef FDI *PFDI; /* pfdi */ + + +#define PFDIfromHFDI(hfdi) ((PFDI)(hfdi)) +#define HFDIfromPFDI(pfdi) ((HFDI)(pfdi)) + +#ifdef ASSERT +#define sigFDI MAKESIG('F','D','I','X') // FDI signature +#define AssertFDI(pfdi) AssertStructure(pfdi,sigFDI); +#else // !ASSERT +#define AssertFDI(pfdi) +#endif // !ASSERT + + +//** Internal function prototypes + +BOOL doCabinetInfoNotify(PFDI pfdi); +BOOL InitFolder(PFDI pfdi, UINT iFolder); +BOOL FDIReadCFFILEEntry(PFDI pfdi); +BOOL FDIReadCFDATAEntry(PFDI pfdi, UINT cbPartial); +BOOL FDIReadPSZ(char *pb, int cb, PFDI pfdi); +BOOL FDIGetFile(PFDI pfdi); +BOOL FDIGetDataBlock(PFDI pfdi); +BOOL SwitchToNewCab(PFDI pfdi); +BOOL LoginCabinet(PFDI pfdi, char *pszCabinet, USHORT setID, USHORT iCabinet); +BOOL SeekFolder(PFDI pfdi,UINT iFolder); +BOOL SetDecompressionType(TCOMP typeCompress,PFDI pfdi); +BOOL MDIDestroyDecompressionGlobal(PFDI pfdi); +BOOL MDICreateDecompressionGlobal(PFDI pfdi); +BOOL MDIResetDecompressionGlobal(PFDI pfdi); +BOOL MDIDecompressGlobal(PFDI pfdi, USHORT *pcbData); + + +/*** FDICreate - Create an FDI context + * + * NOTE: See fdi_int.h for entry/exit conditions. + */ +HFDI FAR DIAMONDAPI FDICreate(PFNALLOC pfnalloc, + PFNFREE pfnfree, + PFNOPEN pfnopen, + PFNREAD pfnread, + PFNWRITE pfnwrite, + PFNCLOSE pfnclose, + PFNSEEK pfnseek, + int cpuType, + PERF perf) +{ + PFDI pfdi; + + pfdi = (PFDI)pfnalloc(sizeof (FDI)); + if (pfdi == NULL) { + ErfSetCodes(perf,FDIERROR_ALLOC_FAIL,0); + return NULL; + } + + SetAssertSignature(pfdi,sigFDI); + AssertFDI(pfdi); // Make sure we've really set sig + + pfdi->pfnfree = pfnfree; // Save free function in our context + pfdi->pfnalloc = pfnalloc; // and all other passed in functions + pfdi->pfnopen = pfnopen; + pfdi->pfnread = pfnread; + pfdi->pfnwrite = pfnwrite; + pfdi->pfnclose = pfnclose; + pfdi->pfnseek = pfnseek; + pfdi->cpuType = cpuType; + pfdi->perf = perf; // and error reporting structure + + pfdi->typeCompress = tcompBAD; // no decompressor initialized + + //** Don't know how big reserved sections are yet, so we cannot allocate + // the buffers. Set bad sizes to help catch errors. LoginCabinet + // will get the sizes from the CFHEADER/CFRESERVE structures in the + // cabinet file and will allocate the buffers. + + pfdi->pcfdata = NULL; + pfdi->pcffolder = NULL; + pfdi->pbCFHeaderReserve = NULL; + + pfdi->cbCFHeaderReserve = cbCF_HEADER_BAD; // Bad value + pfdi->cbCFDataPlusReserve = 0xFFFF; // Bad value + pfdi->cbCFFolderPlusReserve = 0xFFFF; // Bad value + + //** Remember that cabinet file handles are closed + pfdi->hfCabFiles = -1; + pfdi->hfCabData = -1; + + //** Success + return HFDIfromPFDI(pfdi); +} + + +/*** FDIDestroy - Destroy an FDI context + * + * NOTE: See fdi_int.h for entry/exit conditions. + */ +BOOL FAR DIAMONDAPI FDIDestroy(HFDI hfdi) +{ + PFDI pfdi; + + pfdi = PFDIfromHFDI (hfdi); + AssertFDI (pfdi); + + SetDecompressionType(tcompBAD,pfdi); // free the decompressor + + //** Free cabinet structure buffers d + if (pfdi->pbCFHeaderReserve) { + (*pfdi->pfnfree)(pfdi->pbCFHeaderReserve); + } + + if (pfdi->pcffolder) { + (*pfdi->pfnfree)(pfdi->pcffolder); + } + + if (pfdi->pcfdata) { + (*pfdi->pfnfree)(pfdi->pcfdata); + } + + //** Make sure any open file handles are closed + if (pfdi->hfCabFiles != -1) { + pfdi->pfnclose(pfdi->hfCabFiles); // close File Cab File + } + if (pfdi->hfCabData != -1) { + pfdi->pfnclose(pfdi->hfCabData); // close Data Cab File + } + + ClearAssertSignature(pfdi); + pfdi->pfnfree(pfdi); // free our data + + return TRUE; +} + + +/*** FDIIsCabinet - Determines if file is a cabinet, returns info if it is + * + * NOTE: See fdi_int.h for entry/exit conditions. + */ +BOOL FAR DIAMONDAPI FDIIsCabinet(HFDI hfdi, + int hf, + PFDICABINETINFO pfdici) +{ + CFHEADER cfheader; + PFDI pfdi; + + pfdi = PFDIfromHFDI (hfdi); + AssertFDI(pfdi); + + //** Read the CFHEADER structure + if (sizeof(CFHEADER) != pfdi->pfnread(hf, + &cfheader, + sizeof(CFHEADER))) { + return FALSE; // Too small or error on I/O + } + + //** Make sure this is a cabinet file + if (cfheader.sig != sigCFHEADER) { + return FALSE; // Signature does not match + } + + //** Make sure we know this version number + if (cfheader.version != verCF) { + //** Set the error in this case, so client knows version is wrong + ErfSetCodes(pfdi->perf,FDIERROR_UNKNOWN_CABINET_VERSION, + cfheader.version); // return version found + return FALSE; + } + + //** Return cabinet info to caller + pfdici->cbCabinet = cfheader.cbCabinet; + pfdici->cFolders = cfheader.cFolders; + pfdici->cFiles = cfheader.cFiles; + pfdici->setID = cfheader.setID; + pfdici->iCabinet = cfheader.iCabinet; + pfdici->fReserve = (cfheader.flags & cfhdrRESERVE_PRESENT) != 0; + pfdici->hasprev = (cfheader.flags & cfhdrPREV_CABINET); + pfdici->hasnext = (cfheader.flags & cfhdrNEXT_CABINET); + return TRUE; +} /* FDIIsCabinet() */ + + +/*** FDICopy - extracts files from a cabinet + * + * NOTE: See fdi_int.h for entry/exit conditions. + */ +BOOL FAR DIAMONDAPI FDICopy(HFDI hfdi, + char *pszCabinet, + char *pszCabPath, + int flags, + PFNFDINOTIFY pfnfdin, + PFNFDIDECRYPT pfnfdid, + void *pvUser) +{ + PFDINOTIFICATION pfdin; + PFDI pfdi; + BOOL rc=FALSE; // Assume error + + pfdi = PFDIfromHFDI (hfdi); + AssertFDI(pfdi); + pfdin = &pfdi->fdin; + + //** Save call back info for use by other functions + pfdi->pvUser = pvUser; + pfdi->pfnfdin = pfnfdin; + pfdi->pfnfdid = pfnfdid; + + pfdi->cFilesSkipped = 0; // we haven't skipped any CONTD_FOR files yet + strcpy(pfdi->szCabPath,pszCabPath); // make a local copy of cabinet path + + //** Get cabinet; skip setID/iCabinet check + if (!LoginCabinet(pfdi,pszCabinet,0,iCABINET_BAD)) { // Get cabinet + goto error; // error already filled in + } + + pfdi->fInContin = 0; // Not in continuation + pfdi->iFolder = 0xFFFF; // No folder being used yet + strcpy(pfdi->szCabPath,pszCabPath); // save path to cabinet + + while (pfdi->cFilesRemain--) { + if (!FDIReadCFFILEEntry(pfdi)) { + goto error; // error already filled in + } + //** Now copy stuff that is needed on a notification callback + pfdin->psz1 = pfdi->achName; // pass name of the file + pfdin->cb = pfdi->cffile.cbFile; // pass size of file + pfdin->psz2 = pfdi->achCabinetFirst; // name of First Cab (if app.) + pfdin->psz3 = pfdi->achDiskFirst; // name of First Disk (if app.) + pfdin->date = pfdi->cffile.date; // pass date + pfdin->time = pfdi->cffile.time; // pass time + pfdin->attribs = pfdi->cffile.attribs; // pass attributes + pfdin->pv = pfdi->pvUser; + + if (IS_CONTD_BACK(pfdi->cffile.iFolder)) { // continued back? + if (pfdi->fInContin) { // in a continuation cab? + //** We're in a continuation cabinet, and have a continued- + // back candidate. Ask the caller if he wants it, and copy + // it if so. + pfdi->hfNew = pfnfdin(fdintCOPY_FILE,pfdin); + if (pfdi->hfNew == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + goto error; + } + if (pfdi->hfNew) { + if (!FDIGetFile(pfdi)) { // Get the file written into hfNew + goto error; // error already filled in + } + } + else { // caller elected to skip this file + if (IS_CONTD_FORWARD(pfdi->cffile.iFolder)) { + pfdi->cFilesSkipped++; // count forward files skipped + } + } + } + else { + //** If a file is continued-back, but we're not in a + // continuation cabinet, the proper behavior is just to + // notify the caller that we're skipping the file. + if (-1 == pfnfdin(fdintPARTIAL_FILE,pfdin)) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + goto error; + } + } + } + else { // not continued-back + if (!pfdi->fInContin) { // in original cab? + //** This is a normal (not continued-back) file in the + // non-continued cabinet, so ask the user if he wants it, + // and copy it if so. + pfdi->hfNew = pfnfdin(fdintCOPY_FILE,pfdin); + if (pfdi->hfNew == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + goto error; + } + if (pfdi->hfNew) { + if (!FDIGetFile(pfdi)) { // Get the file written into hfNew + goto error; // error already filled in + } + } + else { // caller elected to skip this file + if (IS_CONTD_FORWARD(pfdi->cffile.iFolder)) { + pfdi->cFilesSkipped++; // count forward files skipped + } + } + } // ignore non-continued-back files in continuation cabs + } + } // end while cFilesRemain + + //** Success + rc = TRUE; + +error: + //** Ignore errors closing cabinet files. There is potential that this + // error may occur if a network goes down, etc., and there is no harm + // done. + if (pfdi->hfCabFiles != -1) { + pfdi->pfnclose(pfdi->hfCabFiles); // close File Cab File + } + + if (pfdi->hfCabData != -1) { + pfdi->pfnclose(pfdi->hfCabData); // close Data Cab File + } + + //** Remember that cabinet file handles are closed + pfdi->hfCabFiles = -1; + pfdi->hfCabData = -1; + + return rc; +} + + +/*** LoginCabinet - Make a cabinet current + * + * Entry: + * pfdi - pointer to FDI context + * pszCabinet - pointer to cabinet path/name + * setID - required setID (only if iCabinet == iCABINET_BAD) + * iCabinet - required iCabinet; check that setID/iCabinet match + * cabinet; Set iCabinet == iCABINET_BAD to skip this check. + * + * Exit-Success: + * returns TRUE + * pfdi->cfheader is filled in + * pfdi->hfCabFiles points to first CFFILE entry + * pfdi->cFilesRemain initialized from CFHEADER + * + * Exit-Failure: + * returns FALSE, cabinet was not requested setID/iCabinet, or other + * errors -- perr filled in. + */ +BOOL LoginCabinet(PFDI pfdi, char *pszCabinet, USHORT setID, USHORT iCabinet) +{ + CFHEADER cfheader; + CFRESERVE cfreserve; + UINT cbCFFolderPlusReserve; + UINT cbCFDataPlusReserve; + + AssertFDI(pfdi); + + strcpy(pfdi->szCabName,pfdi->szCabPath); // build path to cabinet + strcat(pfdi->szCabName,pszCabinet); + + Assert(pfdi->hfCabFiles == -1); + Assert(pfdi->hfCabData == -1); + + /** NOTE: It is very important to avoid changing any globals (besides + * the cabinet file handles) until we have verified this cabinet + * is the requested one! + */ + if ((-1 == (pfdi->hfCabFiles=pfdi->pfnopen(pfdi->szCabName, + _O_BINARY | _O_RDONLY, + _S_IREAD | _S_IWRITE ))) + + // Yes, Virginia, we're going to open the same file again, so that + // we can read the CFFILE and CFDATA streams separately, and so we + // can switch to a different cabinet for the remaining data streams + // We could probably use dup() instead, but that would require our + // caller to support it as a callback, so we'll just do another open. + + || (-1 == (pfdi->hfCabData=pfdi->pfnopen(pfdi->szCabName, + _O_BINARY | _O_RDONLY, + _S_IREAD | _S_IWRITE )))) { + //** We don't have a specfic error code from the caller's function + ErfSetCodes(pfdi->perf,FDIERROR_CABINET_NOT_FOUND,0); + return FALSE; + } + + //** Read the CFHEADER structure + if (sizeof(CFHEADER) != pfdi->pfnread(pfdi->hfCabFiles, + &cfheader, + sizeof(CFHEADER))) { + //** We don't have a specfic error code from the caller's function + ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0); + return FALSE; + } + + //** Make sure this is a cabinet file + if (cfheader.sig != sigCFHEADER) { + //** We don't have a specfic error code from the caller's function + ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0); + return FALSE; + } + + //** Make sure we know this version number + if (cfheader.version != verCF) { + ErfSetCodes(pfdi->perf,FDIERROR_UNKNOWN_CABINET_VERSION, + cfheader.version); // return version found + return FALSE; + } + + //** Check setID/iCabinet, if we are asked to + if ((iCabinet != iCABINET_BAD) && // Need to to check setID/iCabinet + ((setID != cfheader.setID) || // SetIDs don't match + (iCabinet != cfheader.iCabinet))) { // or cabinet numbers don't match + //** Not the cabinet that was requested + ErfSetCodes(pfdi->perf,FDIERROR_WRONG_CABINET,0); + return FALSE; + } + + /** OK, this cabinet looks like the one we want. + * Store the cfheader in our state structure. + */ + pfdi->cfheader = cfheader; + + //** Assume there is no CFRESERVE section + cfreserve.cbCFHeader = 0; + cfreserve.cbCFFolder = 0; + cfreserve.cbCFData = 0; + + //** Now read in the CFRESERVE section (if present) + if (pfdi->cfheader.flags & cfhdrRESERVE_PRESENT) { + //** Read the CFRESERVE first + if (sizeof(CFRESERVE) != pfdi->pfnread(pfdi->hfCabFiles, + &cfreserve, + sizeof(CFRESERVE))) { + //** We don't have a specfic error code from the caller's function + ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0); + return FALSE; + } + + //** Make sure CFRESERVE fields seem valid + Assert(cfreserve.cbCFHeader <= cbRESERVE_HEADER_MAX); + Assert(cfreserve.cbCFFolder <= cbRESERVE_FOLDER_MAX); + Assert(cfreserve.cbCFData <= cbRESERVE_DATA_MAX); + + //** Allocate buffer for reserved portion of CF header, if necessary + if (pfdi->cbCFHeaderReserve == cbCF_HEADER_BAD) { + //** First header we are reading + pfdi->cbCFHeaderReserve = cfreserve.cbCFHeader; // Remember size + if (pfdi->cbCFHeaderReserve > 0) { // Need to allocate buffer + if (!(pfdi->pbCFHeaderReserve = + (*pfdi->pfnalloc)(pfdi->cbCFHeaderReserve))) { + ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0); + return FALSE; + } + } + } + + //** Read RESERVE data area for CFHEADER, if present + if (pfdi->cbCFHeaderReserve > 0) { // Need to read reserved data + if (pfdi->cbCFHeaderReserve != pfdi->pfnread(pfdi->hfCabFiles, + pfdi->pbCFHeaderReserve, + pfdi->cbCFHeaderReserve)) { + //** No specfic error code from the caller's function + ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0); + return FALSE; + } + } + } + + //** Compute size of CFFOLDER buffer and allocate it if necessary + cbCFFolderPlusReserve = cfreserve.cbCFFolder + sizeof(CFFOLDER); + if (!pfdi->pcffolder) { // First time, need to allocate buffer + pfdi->cbCFFolderPlusReserve = cbCFFolderPlusReserve; // Full size + if (!(pfdi->pcffolder = (*pfdi->pfnalloc)(pfdi->cbCFFolderPlusReserve))) { + ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0); + return FALSE; + } + } + else { + //** Make sure this folder has same reserve size as last folder! + if (cbCFFolderPlusReserve != pfdi->cbCFFolderPlusReserve) { + ErfSetCodes(pfdi->perf,FDIERROR_RESERVE_MISMATCH,0); + return FALSE; + } + } + + //** Compute size of CFDATA buffer and allocate it if necessary + // NOTE: It is important not to touch the CFDATA block if already + // allocated, because our caller depends upon the old data + // staying there across cabinet boundaries in order to handle + // CFDATA blocks that are split across cabinet boundaries! + // + cbCFDataPlusReserve = cfreserve.cbCFData + sizeof(CFDATA); + if (!pfdi->pcfdata) { // First time, need to allocate buffer + pfdi->cbCFDataPlusReserve = cbCFDataPlusReserve; // Full size + if (!(pfdi->pcfdata = (*pfdi->pfnalloc)(pfdi->cbCFDataPlusReserve))) { + ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0); + return FALSE; + } + } + else { + //** Make sure this Data has same reserve size as last Data! + if (cbCFDataPlusReserve != pfdi->cbCFDataPlusReserve) { + ErfSetCodes(pfdi->perf,FDIERROR_RESERVE_MISMATCH,0); + return FALSE; + } + } + + //** Now read in the back/forward continuation fields, if present + if (pfdi->cfheader.flags & cfhdrPREV_CABINET) { + if (!FDIReadPSZ(pfdi->achCabinetFirst,CB_MAX_CABINET_NAME,pfdi) + || !FDIReadPSZ(pfdi->achDiskFirst,CB_MAX_DISK_NAME,pfdi)) { + return FALSE; // Error already filled in + } + } + else { + //** No previous disk/cabinet + pfdi->achCabinetFirst[0] = '\0'; + pfdi->achDiskFirst[0] = '\0'; + } + + if (pfdi->cfheader.flags & cfhdrNEXT_CABINET) { + if (!FDIReadPSZ(pfdi->achCabinetNext,CB_MAX_CABINET_NAME,pfdi) + || !FDIReadPSZ(pfdi->achDiskNext,CB_MAX_DISK_NAME,pfdi)) { + return FALSE; // Error already filled in + } + } + else { + //** No next disk/cabinet + pfdi->achCabinetNext[0] = '\0'; + pfdi->achDiskNext[0] = '\0'; + } + + //** Remember base of folder entries + pfdi->coffFolders = pfdi->pfnseek(pfdi->hfCabFiles,0L,SEEK_CUR); + if (-1L == pfdi->coffFolders) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; + } + + //** Seek to first CFFILE entry + if (-1L == pfdi->pfnseek(pfdi->hfCabFiles, + pfdi->cfheader.coffFiles, + SEEK_SET)) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; + } + + pfdi->cFilesRemain = pfdi->cfheader.cFiles; + + //** Tell client what the *next* cabinet is + if (!doCabinetInfoNotify(pfdi)) { + return FALSE; + } + + //** Success + return TRUE; +} + + +/*** FDIGetFile - Extract one individual file from cabinet. + * + * Entry: + * pfdi - pointer to FDI context + * pfdi->hfNew - open file handle to write to + * pfdi->cffile - filled in + * + * Exit-Success: + * returns TRUE, output file closed + * + * Exit-Failure: + * returns FALSE, output file closed, error structure filled in + */ +BOOL FDIGetFile(PFDI pfdi) +{ + UOFF uoffFile; + UOFF cbFileRemain; + UINT cbWriteBase; + UINT cbWrite; + PFDINOTIFICATION pfdin; + int rcClose; + + //** First, we must make sure we have selected the correct folder + AssertFDI(pfdi); + if (!InitFolder(pfdi,pfdi->cffile.iFolder)) { + goto error; // error code already filled in + } + + uoffFile = pfdi->cffile.uoffFolderStart; // init file pointer + cbFileRemain = pfdi->cffile.cbFile; // init file counter + + + //jeffwe: Only bypass if there is something to bypass + if (cbFileRemain) { + //** Now bypass any unwanted data blocks + while (uoffFile >= (pfdi->uoffFolder + pfdi->pcfdata->cbUncomp)) { + if (!FDIGetDataBlock(pfdi)) { // get next data block + goto error; // error code already filled in + } + } + } + + //** Okay, now we have the first block. Write it, and any that + // remain, into our target file + while (cbFileRemain) { + cbWriteBase = (UINT)(uoffFile - pfdi->uoffFolder); + cbWrite = pfdi->pcfdata->cbUncomp - cbWriteBase; // size left in block + if ((ULONG)cbWrite > cbFileRemain) { + cbWrite = (UINT)cbFileRemain; + } + + if (cbWrite != pfdi->pfnwrite(pfdi->hfNew, + &pfdi->pchUncompr[cbWriteBase], + cbWrite)) { + ErfSetCodes(pfdi->perf,FDIERROR_TARGET_FILE,0); + goto error; // couldn't write + } + + uoffFile += cbWrite; + cbFileRemain -= cbWrite; + + //** Get another block? + if (cbFileRemain && !FDIGetDataBlock(pfdi)) { + goto error; // error code already filled in + } + } + + pfdin = &pfdi->fdin; + pfdin->psz1 = pfdi->achName; // pass name of file + pfdin->hf = pfdi->hfNew; // pass file handle + pfdin->date = pfdi->cffile.date; // pass date + pfdin->time = pfdi->cffile.time; // pass time + pfdin->attribs = pfdi->cffile.attribs; // pass attributes + pfdin->pv = pfdi->pvUser; // pass callback context + pfdin->cb = 0; // Default as don't run after ext. + + if (pfdin->attribs & RUNATTRIB) { + pfdin->cb = 1; // Run This after extraction + pfdin->attribs &= ~RUNATTRIB; // Clear the flag + } + + + //** Let client close file and set file date/time/attributes + rcClose = pfdi->pfnfdin(fdintCLOSE_FILE_INFO,pfdin); + if (rcClose == -1) { + //** NOTE: We assume that the client *did* close the file! + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + goto error; + } + pfdi->hfNew = -1; // Remember that file is closed + + if (!rcClose) { + ErfSetCodes(pfdi->perf,FDIERROR_TARGET_FILE,0); + return FALSE; + } + return TRUE; + +error: + //** Make sure output file is closed + if (pfdi->hfNew != -1) { + pfdi->pfnclose(pfdi->hfNew); // Close it + pfdi->hfNew = -1; // Remember that file is closed + } + return FALSE; +} + + +/*** FDIGetDataBlock -- read next CFDATA entry, decompress + * + * Entry: + * pfdi -- FDI context + * + * Exit-success: + * returns TRUE; + * pfdi->uoffFolder has *previous* block added to it + * new data block ready in pfdi->pchUncompr + * + * Exit-failure: + * returns FALSE, error code filled in + */ +BOOL FDIGetDataBlock(PFDI pfdi) +{ + USHORT cbResult; + + AssertFDI(pfdi); + pfdi->uoffFolder += pfdi->pcfdata->cbUncomp; // update uncompr base ptr + + if (pfdi->cCFDataRemain == 0) { + if (!SwitchToNewCab(pfdi)) { + return FALSE; // error already filled in + } + } + + pfdi->cCFDataRemain--; + if (!FDIReadCFDATAEntry(pfdi,0)) { + return FALSE; // error already filled in + } + + //** Is this a continued-forward block? + if (pfdi->pcfdata->cbUncomp == 0) { + //** Switch to new cabinet and read second piece of data block + if ((!SwitchToNewCab(pfdi)) + || (!FDIReadCFDATAEntry(pfdi,pfdi->pcfdata->cbData))) { + return FALSE; // error already filled in + } + } + + cbResult = pfdi->pcfdata->cbUncomp; // Expected uncompressed size + if (!MDIDecompressGlobal(pfdi,&cbResult)) { + return FALSE; // error already filled in + } + + if (cbResult != pfdi->pcfdata->cbUncomp) { + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; // wrong length after decompress + } + return TRUE; +} + + +/*** SwitchToNewCab -- move on to the continuation cabinet + * + * Entry: + * pfdi -- FDI context pointer + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error filled in + */ +BOOL SwitchToNewCab(PFDI pfdi) +{ + BOOL fWrongCabinet; + USHORT iCabinet; + PFDINOTIFICATION pfdin; + USHORT setID; + + AssertFDI(pfdi); + Assert(pfdi->hfCabData != -1); + Assert(pfdi->hfCabFiles != -1); + + //** Remember cabinet info so we can make sure we get the correct + // continuation cabinet + setID = pfdi->cfheader.setID; + iCabinet = pfdi->cfheader.iCabinet + 1; + + pfdin = &pfdi->fdin; + pfdin->psz1 = pfdi->achCabinetNext; // pass name of next cab + pfdin->psz2 = pfdi->achDiskNext; // pass name of next disk + pfdin->psz3 = pfdi->szCabPath; // allow cabinet path to change + pfdin->pv = pfdi->pvUser; // pass callback context + pfdin->setID = setID; // required setID + pfdin->iCabinet = iCabinet; // required iCabinet + pfdin->fdie = FDIERROR_NONE; // No error + + //** Get continuation cabinet + do { + fWrongCabinet = FALSE; // Assume we will get the right cabinet + + //** Make sure cabinet file handles are closed + if (((pfdi->hfCabData != -1) && pfdi->pfnclose(pfdi->hfCabData)) || + ((pfdi->hfCabFiles != -1) && pfdi->pfnclose(pfdi->hfCabFiles))) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; // couldn't close old cabinet + } + //** Remember they are closed + pfdi->hfCabFiles = -1; + pfdi->hfCabData = -1; + + //** Ask client for next cabinet + if (pfdi->pfnfdin(fdintNEXT_CABINET,pfdin) == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + return FALSE; // Client aborted + } + + //** Open next cabinet + if ((!LoginCabinet(pfdi,pfdi->achCabinetNext,setID,iCabinet)) + || (!SeekFolder(pfdi,0))) { // select following folder + //** Don't bail unless explicitly told to + if (pfdi->perf->erfOper == FDIERROR_USER_ABORT) { + return FALSE; // error already filled in + } + //** Have to call fdintNEXT_CABINET again + fWrongCabinet = TRUE; + } + + //** Pass error code to fdintNEXT_CABINET (if we call again) + pfdin->fdie = pfdi->perf->erfOper; + } + while (fWrongCabinet); // Keep going until we get right one + + //** Skip over CFFILE entries that are dups from previous cabinet + pfdi->cFilesSkipped++; // skip file we're doing right now, too + while (pfdi->cFilesSkipped) { + pfdi->cFilesRemain--; + pfdi->cFilesSkipped--; + if (!FDIReadCFFILEEntry(pfdi)) { + return FALSE; // error code already filled in + } + } + + pfdi->fInContin = TRUE; + return TRUE; +} + + +/*** doCabinetInfoNotify - pass back info on next cabinet to client + * + * Entry: + * pfdi - FDI context pointer + * + * Exit-Success: + * returns TRUE + * + * Exit-Failure: + * returns FALSE, error filled in + */ +BOOL doCabinetInfoNotify(PFDI pfdi) +{ + PFDIDECRYPT pfdid; + PFDINOTIFICATION pfdin; + + //** Set info for next cabinet and pass back to client + AssertFDI(pfdi); + pfdin = &pfdi->fdin; + pfdid = &pfdi->fdid; + + //** Notify extract code + pfdin->psz1 = pfdi->achCabinetNext; // pass name of next cab + pfdin->psz2 = pfdi->achDiskNext; // pass name of next disk + pfdin->psz3 = pfdi->szCabPath; // cabinet filespec + pfdin->pv = pfdi->pvUser; // pass callback context + pfdin->setID = pfdi->cfheader.setID; + pfdin->iCabinet = pfdi->cfheader.iCabinet; + if (pfdi->pfnfdin(fdintCABINET_INFO,pfdin) == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + return FALSE; // user aborted + } + + //** Notify decrypt code, if callback was supplied + if (pfdi->pfnfdid != NULL) { + pfdid->fdidt = fdidtNEW_CABINET; + pfdid->pvUser = pfdi->pvUser; + pfdid->cabinet.pHeaderReserve = pfdi->pbCFHeaderReserve; + pfdid->cabinet.cbHeaderReserve = pfdi->cbCFHeaderReserve; + pfdid->cabinet.setID = pfdi->cfheader.setID; + pfdid->cabinet.iCabinet = pfdi->cfheader.iCabinet; + if (pfdi->pfnfdid(pfdid) == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + return FALSE; // user aborted + } + } + return TRUE; +} /* doCabinetInfoNotify() */ + + +/*** InitFolder - make sure desired folder is ready to read + * + * Entry: + * pfdi -> general context pointer + * iFolder = iFolder field from CFFILE entry + * if iFolder == -1, then this is a continuation of prev. folder + * + * Exit-Success: + * Returns TRUE + * + * Exit-Failure: + * Returns FALSE, error code filled in + * + * Note: + * If we're in a continuation cabinet, this function will just + * set iFolder=0 and return. All folder initialization of future + * folders (in continuation cabinets) requires slightly different + * logic and is done elsewhere. + */ +BOOL InitFolder(PFDI pfdi,UINT iFolder) +{ + if (pfdi->fInContin) { + iFolder = 0; + return TRUE; + } + + if (IS_CONTD_FORWARD(iFolder)) { + iFolder = pfdi->cfheader.cFolders-1; + } + + if (pfdi->iFolder != iFolder) { + if ((!MDIResetDecompressionGlobal(pfdi)) + || (!SeekFolder(pfdi,iFolder))) { + return FALSE; // error already filled in + } + + if (!FDIGetDataBlock(pfdi)) { // get the first data block into buffer + return FALSE; // bail if error + } + //** Start at offset zero in uncompressed space + pfdi->uoffFolder = 0; + } + + return TRUE; +} + + +/*** SeekFolder - Seek open cabinet to iFolder, prepare to read data + * + * Entry: + * pfdi - FDI context + * iFolder - Folder number to open + * + * Exit-success: + * return TRUE; pfdi->pcffolder filled in; file position updated + * + * Exit-failure: + * return FALSE, error code filled in + */ +BOOL SeekFolder(PFDI pfdi,UINT iFolder) +{ + PFDIDECRYPT pfdid; + + AssertFDI(pfdi); + pfdid = &pfdi->fdid; + pfdi->iFolder = iFolder; + + //** Read folder and position file pointer to first CFFOLDER block + if ((-1L == pfdi->pfnseek(pfdi->hfCabData, + pfdi->coffFolders + + iFolder*pfdi->cbCFFolderPlusReserve, + SEEK_SET)) // seek to CFFOLDER entry + + //** Read CFFOLDER + reserved area + || (pfdi->cbCFFolderPlusReserve != + (UINT)pfdi->pfnread(pfdi->hfCabData, + pfdi->pcffolder, + pfdi->cbCFFolderPlusReserve)) + + //** Seek to first CFDATA of this folder + || (-1L == pfdi->pfnseek(pfdi->hfCabData, + pfdi->pcffolder->coffCabStart, + SEEK_SET))) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; // problem accessing cabinet file + } + + pfdi->cCFDataRemain = pfdi->pcffolder->cCFData; + +#ifdef BIT16 +//** 09-Jun-1994 bens Turned on Quantum library! +// #define NO_QUANTUM_16 1 +#endif + + if (!SetDecompressionType(pfdi->pcffolder->typeCompress,pfdi)) { + return FALSE; // error already filled in + } + + //** Notify decrypt code, if callback was supplied + if (pfdi->pfnfdid != NULL) { + pfdid->fdidt = fdidtNEW_FOLDER; + pfdid->pvUser = pfdi->pvUser; + //** Point to per folder reserved area + Assert(pfdi->cbCFFolderPlusReserve >= sizeof(CFFOLDER)); + pfdid->folder.cbFolderReserve = pfdi->cbCFFolderPlusReserve + - sizeof(CFFOLDER); + if (pfdid->folder.cbFolderReserve > 0) { + pfdid->folder.pFolderReserve = ((BYTE *)pfdi->pcffolder) + + sizeof(CFFOLDER); + } + else { + pfdid->folder.pFolderReserve = NULL; // No reserved data + } + pfdid->folder.iFolder = iFolder; + if (pfdi->pfnfdid(pfdid) == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + return FALSE; // user aborted + } + } + return TRUE; +} + + +/*** FDIReadCFFILEEntry -- Read in a complete CFFILE entry + * + * Entry: + * pfdi -- pointer to FDI context + * + * Exit success: + * pfdi->cffile structure filled in + * returns TRUE + * + * Exit failure: + * returns FALSE if couldn't read a CFFILE entry, perf filled in + * + * Note: + * unlike in this routine's counterpart in FCI, it is a fatal + * error if we hit an eof, because at this point, we never call + * this routine unless we know it can expect a valid entry + */ +BOOL FDIReadCFFILEEntry(PFDI pfdi) +{ + if ((sizeof(CFFILE) != (unsigned) pfdi->pfnread(pfdi->hfCabFiles, + &pfdi->cffile, + sizeof (CFFILE))) + || !FDIReadPSZ(pfdi->achName, + CB_MAX_FILENAME, + pfdi)) + { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; // couldn't get a full valid CFFILE record + } + + return TRUE; +} + + +/*** FDIReadCFDATAEntry - Read in a complete CFDATA entry + * + * Entry: + * pfdi - Pointer to FDI context (for file i/o) + * cbPartial - Amount of data already present in our local data + * buffer (pfdi->pchCompr). 0 means we haven't + * read any data yet for this block; greater than + * 0 means we've read the first portion of a split + * block, and we're reading the second piece now. + * + * Exit-Success: + * cfdata structure filled in + * return code TRUE + * + * Exit-Failure: + * return code FALSE, error structure filled in + * + * Notes: + * Unlike this routine's counterpart in the FCI stuff, it is a fatal + * error here if we get an EOF. That is because FDI knows for sure + * how many entries there should be and never tries to read too much. + */ +int FDIReadCFDATAEntry(PFDI pfdi, UINT cbPartial) +{ + BOOL fSplit; // TRUE if this is read of the first + // or second piece of a split block! + PFDIDECRYPT pfdid; + CHECKSUM calcsum; + + AssertFDI(pfdi); + pfdid = &pfdi->fdid; + + //** Read CFDATA plus reserved section + if ((pfdi->cbCFDataPlusReserve != pfdi->pfnread(pfdi->hfCabData, + pfdi->pcfdata, + pfdi->cbCFDataPlusReserve)) + + //** Make sure amount read is not larger than compressed data buffer + || ((pfdi->pcfdata->cbData + cbPartial) > pfdi->cbMaxCompr) + + //** Read actual data itself + || ((pfdi->pcfdata->cbData != pfdi->pfnread(pfdi->hfCabData, + &pfdi->pchCompr[cbPartial], + pfdi->pcfdata->cbData)))) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; // no valid record available + } + + + //** JEFFWE - Check CRC if it is set + if (pfdi->pcfdata->csum != 0) { + calcsum = CSUMCompute(&(pfdi->pcfdata->cbData), + pfdi->cbCFDataPlusReserve - sizeof(CHECKSUM), + CSUMCompute(&(pfdi->pchCompr[cbPartial]), + pfdi->pcfdata->cbData, + 0 + ) + ); + if (calcsum != pfdi->pcfdata->csum) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; + } + } + + + + pfdi->pcfdata->cbData += cbPartial; // make it look like one whole block + + //** Determine if this is a split data block + fSplit = (cbPartial > 0) // Second piece of split block + || (pfdi->pcfdata->cbUncomp == 0); // First piece of split block + + //** Notify decrypt code, if callback was supplied + if (pfdi->pfnfdid != NULL) { + pfdid->fdidt = fdidtDECRYPT; + pfdid->pvUser = pfdi->pvUser; + //** Point to per data block reserved area + Assert(pfdi->cbCFDataPlusReserve >= sizeof(CFDATA)); + pfdid->decrypt.cbDataReserve = pfdi->cbCFDataPlusReserve + - sizeof(CFDATA); + if (pfdid->decrypt.cbDataReserve > 0) { + pfdid->decrypt.pDataReserve = ((BYTE *)pfdi->pcfdata) + + sizeof(CFDATA); + } + else { + pfdid->decrypt.pDataReserve = NULL; // No reserved data + } + + pfdid->decrypt.pbData = &pfdi->pchCompr[cbPartial]; + pfdid->decrypt.cbData = pfdi->pcfdata->cbData; + pfdid->decrypt.fSplit = fSplit; + pfdid->decrypt.cbPartial = cbPartial; + if (pfdi->pfnfdid(pfdid) == -1) { + ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0); + return FALSE; // user aborted + } + } + return TRUE; +} /* FDIReadCFDATAEntry() */ + + +/*** FDIReadPSZ -- Read in a psz name + * + * Entry: + * pb - buffer to load name into + * cb - maximum legal length for name + * pfdi - pointer to FDI context (for file i/o functions) + * + * Exit-Success: + * Returns TRUE, name filled in + * + * Exit-Failure: + * Returns FALSE (file read error, or string too long) + */ +BOOL FDIReadPSZ(char *pb, int cb, PFDI pfdi) +{ + char chLast; + int cbValue; + int cbRead; + long pos; + + //** Save current position + pos = (*pfdi->pfnseek)(pfdi->hfCabFiles,0,SEEK_CUR); + + //** Read in enough to get longest possible value + cbRead = (*pfdi->pfnread)(pfdi->hfCabFiles,pb,cb); + if (cbRead <= 0) { // At EOF, or an error occured + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; + } + + //** Pick out just ASCIIZ string and adjust file position + chLast = pb[cb-1]; // Save last character + pb[cb-1] = '\0'; // Ensure terminated + cbValue = strlen(pb); // Get string length + if ( ((cbValue+1) >= cb) && (chLast != '\0')) { + //** String filled up buffer and was not null terminated in + // file, so something must be wrong. + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; + } + + //** Position to just past string + if (-1L == (*pfdi->pfnseek)(pfdi->hfCabFiles,pos+cbValue+1,SEEK_SET)) { + ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0); + return FALSE; + } + return TRUE; +} + + +/*** SetDecompressionType -- initializes a new decompressor + * + * Entry: + * typeCompress -- new compression type (tcompBAD to term w/ no new) + * pfdi -- FDI context structure + * + * Exit-success: + * returns TRUE; + * + * Exit-failure: + * returns FALSE, error code filled in + */ +BOOL SetDecompressionType(TCOMP typeCompress,PFDI pfdi) +{ + //** Don't do anything if type is unchanged + if (typeCompress == pfdi->typeCompress) { + return TRUE; + } + + //** Destroy existing decompression context (if any) + if (!MDIDestroyDecompressionGlobal(pfdi)) { + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; + } + + //** Create new decompression context + pfdi->typeCompress = typeCompress; + if (!MDICreateDecompressionGlobal(pfdi)) + { + return FALSE; + } + + return TRUE; +} + + +/*** MDIDestroyDecompressionGlobal -- Destroy the currently selected decompressor + * + * Entry: + * pfdi - pointer to FDI context + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error code filled in + */ +BOOL MDIDestroyDecompressionGlobal(PFDI pfdi) +{ + switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) { + + case tcompBAD: // no existing compression + return TRUE; // nothing to do if there wasn't any compressor + + case tcompTYPE_NONE: + break; //no action needed for null compressor + + case tcompTYPE_MSZIP: + if (MDI_ERROR_NO_ERROR != MDIDestroyDecompression(pfdi->mdh)) { + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; // no valid compressor initialized + } + break; + +#if !defined(BIT16) || !defined(NO_QUANTUM_16) + case tcompTYPE_QUANTUM: + if (MDI_ERROR_NO_ERROR != QDIDestroyDecompression(pfdi->mdh)) { + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; // no valid compressor initialized + } + break; +#endif + + default: + ErfSetCodes(pfdi->perf,FDIERROR_BAD_COMPR_TYPE,0); + return FALSE; + } // end switch + + //** Now free the buffers + pfdi->pfnfree (pfdi->pchCompr); + pfdi->pfnfree (pfdi->pchUncompr); + return TRUE; +} + + +/*** MDICreateDecompressionGlobal -- Create the currently selected decompressor + * + * Entry: + * pfdi -- pointer to FDI context + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error code filled in + * + */ +BOOL MDICreateDecompressionGlobal(PFDI pfdi) +{ + QUANTUMDECOMPRESS qdec; + FDIERROR fdierror = FDIERROR_NONE; + int mdierror; + + pfdi->cbMaxUncompr = CB_MAX_CHUNK; + + /* first pass to establish buffer sizes */ + + switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) { + + case tcompBAD: // no new compressor + return TRUE; // all done if no compressor enabled + + case tcompTYPE_NONE: + pfdi->cbMaxCompr = pfdi->cbMaxUncompr; // for null compr., bufs are same + break; + + case tcompTYPE_MSZIP: + if (MDI_ERROR_NO_ERROR != MDICreateDecompression(&pfdi->cbMaxUncompr, + NULL, + NULL, + &pfdi->cbMaxCompr, + NULL)) + { + fdierror = FDIERROR_MDI_FAIL; + } + break; + +#if !defined(BIT16) || !defined(NO_QUANTUM_16) + case tcompTYPE_QUANTUM: + qdec.WindowBits = CompressionMemoryFromTCOMP(pfdi->typeCompress); + + //** Set CPU type, make sure FDI & QDI definitions match + Assert(QDI_CPU_UNKNOWN == cpuUNKNOWN); + Assert(QDI_CPU_80286 == cpu80286); + Assert(QDI_CPU_80386 == cpu80386); + qdec.fCPUtype = pfdi->cpuType; + + if (MDI_ERROR_NO_ERROR != QDICreateDecompression(&pfdi->cbMaxUncompr, + &qdec, + NULL, + NULL, + &pfdi->cbMaxCompr, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL)) + { + fdierror = FDIERROR_MDI_FAIL; + } + break; +#endif + + default: + fdierror = FDIERROR_BAD_COMPR_TYPE; + } + + if (fdierror != FDIERROR_NONE) + { + ErfSetCodes(pfdi->perf,fdierror,0); + pfdi->typeCompress = tcompBAD; // No compression context + return FALSE; + } + + //** Now allocate whatever buffers the selected compressor requested + + if (NULL == (pfdi->pchCompr = (char *) pfdi->pfnalloc (pfdi->cbMaxCompr))) + { + ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0); + pfdi->typeCompress = tcompBAD; // No compression context + return FALSE; + } + + if (NULL == (pfdi->pchUncompr = (char *) pfdi->pfnalloc (pfdi->cbMaxUncompr))) + { + pfdi->pfnfree(pfdi->pchCompr); + ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0); + pfdi->typeCompress = tcompBAD; // No compression context + return FALSE; + } + + /* second pass to really setup a decompressor */ + + switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) { + + case tcompTYPE_MSZIP: + mdierror = MDICreateDecompression(&pfdi->cbMaxUncompr, + pfdi->pfnalloc, + pfdi->pfnfree, + &pfdi->cbMaxCompr, + &pfdi->mdh); + if (mdierror != MDI_ERROR_NO_ERROR) + { + if (mdierror == MDI_ERROR_NOT_ENOUGH_MEMORY) + { + fdierror = FDIERROR_ALLOC_FAIL; + } + else + { + fdierror = FDIERROR_MDI_FAIL; + } + } + break; + +#if !defined(BIT16) || !defined(NO_QUANTUM_16) + case tcompTYPE_QUANTUM: + mdierror = QDICreateDecompression(&pfdi->cbMaxUncompr, + &qdec, + pfdi->pfnalloc, + pfdi->pfnfree, + &pfdi->cbMaxCompr, + &pfdi->mdh, + pfdi->pfnopen, + pfdi->pfnread, + pfdi->pfnwrite, + pfdi->pfnclose, + pfdi->pfnseek); + if (mdierror != MDI_ERROR_NO_ERROR) + { + if (mdierror == MDI_ERROR_NOT_ENOUGH_MEMORY) + { + fdierror = FDIERROR_ALLOC_FAIL; + } + else + { + fdierror = FDIERROR_MDI_FAIL; + } + } + break; +#endif + } + + if (fdierror != FDIERROR_NONE) + { + pfdi->pfnfree(pfdi->pchCompr); + pfdi->pfnfree(pfdi->pchUncompr); + ErfSetCodes(pfdi->perf,fdierror,0); + pfdi->typeCompress = tcompBAD; // No compression context + return FALSE; + } + + // NOTE: At this point, we leave the compression type set in pfdi, + // so that SetDecompressionType() will clean up the handle + // to the compression context. + + return TRUE; +} + + +/*** MDIResetDecompressionGlobal -- reset the currently selected decompressor + * + * Entry: + * pfdi -- pointer to folder context + * + * Exit-success: + * returns TRUE + * + * Exit-failure: + * returns FALSE, error code filled in + */ +BOOL MDIResetDecompressionGlobal(PFDI pfdi) +{ + switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) { + + case tcompBAD: + break; // no compression selected + + case tcompTYPE_NONE: + break; // no action for null compressor + + case tcompTYPE_MSZIP: + if (MDI_ERROR_NO_ERROR != MDIResetDecompression(pfdi->mdh)) + { + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; // no valid compressor initialized + } + break; + +#if !defined(BIT16) || !defined(NO_QUANTUM_16) + case tcompTYPE_QUANTUM: + if (MDI_ERROR_NO_ERROR != QDIResetDecompression(pfdi->mdh)) + { + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; // no valid compressor initialized + } + break; +#endif + + default: + ErfSetCodes(pfdi->perf,FDIERROR_BAD_COMPR_TYPE,0); + return FALSE; // unknown compression type + } + return TRUE; +} + + +/*** MDIDecompressGlobal - Decompress using currently selected decompressor + * + * Entry: + * pfdi - Pointer to FDI context + * pcbData - Location to return the compressed size; + * NOTE: For Quantum, must contain the EXACT EXPECTED + * UNCOMPRESSED data size! + * + * Exit-Success: + * Returns TRUE + * + * Exit-Failure: + * Returns FALSE, error structure filled in + */ +BOOL MDIDecompressGlobal(PFDI pfdi, USHORT *pcbData) +{ + UINT cbData; + + switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) { + case tcompTYPE_NONE: + memcpy(pfdi->pchUncompr, + pfdi->pchCompr, + *pcbData=pfdi->pcfdata->cbData); + break; // done for null compressor + + case tcompTYPE_MSZIP: + cbData = pfdi->cbMaxUncompr; // Size of destination buffer + if (MDI_ERROR_NO_ERROR != + MDIDecompress(pfdi->mdh, // MDI context + pfdi->pchCompr, // Compressed data + pfdi->pcfdata->cbData, // source buffer size + pfdi->pchUncompr, // Destination buffer + &cbData)) { // resulting data size + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; + } + //** Narrow result (16-bit case) + *pcbData = (USHORT)cbData; + break; + +#if !defined(BIT16) || !defined(NO_QUANTUM_16) + case tcompTYPE_QUANTUM: + cbData = (UINT)*pcbData; // Size of *uncompressed* data! + if (MDI_ERROR_NO_ERROR != + QDIDecompress(pfdi->mdh, // QDI context + pfdi->pchCompr, // Compressed data + pfdi->pcfdata->cbData, // source buffer size + pfdi->pchUncompr, // Destination buffer + &cbData)) { // resulting data size + ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0); + return FALSE; + } + //** Narrow result (16-bit case) + *pcbData = (USHORT)cbData; + break; +#endif + + default: + ErfSetCodes(pfdi->perf,FDIERROR_BAD_COMPR_TYPE,0); + return FALSE; // no valid compressor initialized + } + return TRUE; +} diff --git a/private/windows/diamond/chuck/fdi.nt/makefile b/private/windows/diamond/chuck/fdi.nt/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/chuck/fdi.nt/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/chuck/fdi.nt/sources b/private/windows/diamond/chuck/fdi.nt/sources new file mode 100644 index 000000000..f1aff038e --- /dev/null +++ b/private/windows/diamond/chuck/fdi.nt/sources @@ -0,0 +1,37 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Ted Miller (tedm) 19-Feb-1991 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=diamond +MINORCOMP=fdi + +TARGETNAME=fdi +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +SOURCES=..\fdi.c \ + ..\checksum.c \ + ..\erf.c \ + ..\asrt.c + +UMTYPE=console diff --git a/private/windows/diamond/chuck/fdi_int.h b/private/windows/diamond/chuck/fdi_int.h new file mode 100644 index 000000000..9d3a7d580 --- /dev/null +++ b/private/windows/diamond/chuck/fdi_int.h @@ -0,0 +1,917 @@ +/*** fdi_int.h - Diamond File Decompression Interface definitions + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Chuck Strouss, Benjamin W. Slivka + * + * History: + * 30-Nov-1993 chuckst Created + * 21-Dec-1993 bens Updated with comments from 12/21/93 design review + * 09-Mar-1994 bens Add new error code + * 17-Mar-1994 bens Specify structure packing explicitly + * 21-Mar-1994 bens Spruce up comments + * 25-Mar-1994 bens Add fdintCABINET_INFO notification + * 31-Mar-1994 bens Clarify handling of open files when errors occur + * 01-Apr-1994 bens Add FDIIsCabinet() function. + * 07-Apr-1994 bens Add Decryption interfaces; remove fdintPROGRESS + * 11-Apr-1994 bens Add more guidance on how to respond to FDI errors. + * 13-Apr-1994 bens Add date & time & attribs to fdintCOPY_FILE + * 18-Apr-1994 bens Changed CDECL to DIAMONDAPI + * 05-May-1994 bens Clarified error handling (billhu/alanr/migueldc) + * 11-May-1994 bens Added setId/iCabinet to fdintNEXT_CABINET + * 07-Jul-1994 bens Support Quantum virtual file -- PLEASE note the + * comments about PFNOPEN/PFNCLOSE changes, and + * about reserving memory, if necessary, before + * calling FDICreate()! + * 19-Aug-1994 bens Add cpuType parameter to FDICreate(). + * 03-Apr-1995 jeffwe Added chaining indicators to FDICABINETINFO + * + * + * ATTENTION: + * This is the only documentation on the Diamond File Decompression + * Interface (FDI). Please read it carefully, as there are some subtle + * points in FDI that are carefully explained below. + * + * Concepts: + * A *cabinet* file contains one or more *folders*. A folder contains + * one or more (pieces of) *files*. A folder is by definition a + * decompression unit, i.e., to extract a file from a folder, all of + * the data from the start of the folder up through and including the + * desired file must be read and decompressed. + * + * A folder can span one (or more) cabinet boundaries, and by implication + * a file can also span one (or more) cabinet boundaries. Indeed, more + * than one file can span a cabinet boundary, since Diamond concatenates + * files together into a single data stream before compressing (actually, + * at most one file will span any one cabinet boundary, but Diamond does + * not know which file this is, since the mapping from uncompressed bytes + * to compressed bytes is pretty obscure. Also, since Diamond compresses + * in blocks of 32K (at present), any files with data in a 32K block that + * spans a cabinet boundary require Diamond to read both cabinet files + * to get the two halves of the compressed block). + * + * Overview: + * The File Decompression Interface is used to simplify the reading of + * Diamond cabinet files. A setup program will proceed in a manner very + * similar to the pseudo code below. An FDI context is created, the + * setup program calls FDICopy() for each cabinet to be processed. For + * each file in the cabinet, FDICopy() calls a notification callback + * routine, asking the setup program if the file should be copied. + * This call-back approach is great because it allows the cabinet file + * to be read and decompressed in an optimal manner, and also makes FDI + * independent of the run-time environment -- FDI makes *no* C run-time + * calls whatsoever. All memory allocation and file I/O functions are + * passed into FDI by the client. + * + * main(...) + * { + * // Read INF file to construct list of desired files. + * // Ideally, these would be sorted in the same order as the + * // files appear in the cabinets, so that you can just walk + * // down the list in response to fdintCOPY_FILE notifications. + * + * // Construct list of required cabinets. + * + * hfdi = FDICreate(...); // Create FDI context + * For (cabinet in List of Cabinets) { + * FDICopy(hfdi,cabinet,fdiNotify,...); // Process each cabinet + * } + * FDIDestroy(hfdi); + * ... + * } + * + * // Notification callback function + * fdiNotify(fdint,...) + * { + * If (User Aborted) // Permit cancellation + * if (fdint == fdintCLOSE_FILE_INFO) + * close open file + * return -1; + * switch (fdint) { + * case fdintCOPY_FILE: // File to copy, maybe + * // Check file against list of desired files + * if want to copy file + * open destination file and return handle + * else + * return NULL; // Skip file + * case fdintCLOSE_FILE_INFO: + * close file + * set date, time, and attributes + * + * case fdintNEXT_CABINET: + * if not an error callback + * Tell FDI to use suggested directory name + * else + * Tell user what the problem was, and prompt + * for a new disk and/or path. + * if user aborts + * Tell FDI to abort + * else + * return to FDI to try another cabinet + * //NOTE: Be sure to see the (sample) code in EXTRACT.C + * // for an example of how to do this! + * ... + * } + * + * Error Handling Suggestions: + * Since you the client have passed in *all* of the functions that + * FDI uses to interact with the "outside" world, you are in prime + * position to understand and deal with errors. + * + * The general philosophy of FDI is to pass all errors back up to + * the client. FDI returns fairly generic error codes in the case + * where one of the callback functions (PFNOPEN, PFNREAD, etc.) fail, + * since it assumes that the callback function will save enough + * information in a static/global so that when FDICopy() returns + * fail, the client can examine this information and report enough + * detail about the problem that the user can take corrective action. + * + * For very specific errors (CORRUPT_CABINET, for example), FDI returns + * very specific error codes. + * + * THE BEST POLICY IS FOR YOUR CALLBACK ROUTINES TO AVOID RETURNING + * ERRORS TO FDI! + * + * Examples: + * (1) If the disk is getting full, instead of returning an error + * from your PFNWRITE function, you should -- inside your + * PFNWRITE function -- put up a dialog telling the user to free + * some disk space. + * (2) When you get the fdintNEXT_CABINET notification, you should + * verify that the cabinet you return is the correct one (call + * FDIIsCabinet(), and make sure the setID matches the one for + * the current cabinet specified in the fdintCABINET_INFO, and + * that the disk number is one greater. + * + * NOTE: FDI will continue to call fdintNEXT_CABINET until it + * gets the cabinet it wants, or until you return -1 + * to abort the FDICopy() call. + * + * The documentation below on the FDI error codes provides explicit + * guidance on how to avoid each error. + * + * If you find you must return a failure to FDI from one of your + * callback functions, then FDICopy() frees all resources it allocated + * and closes all files. If you can figure out how to overcome the + * problem, you can call FDICopy() again on the last cabinet, and + * skip any files that you already copied. But, note that FDI does + * *not* maintain any state between FDICopy() calls, other than possibly + * memory allocated for the decompressor. + * + * See FDIERROR for details on FDI error codes and recommended actions. + * + * + * Progress Indicator Suggestions: + * As above, all of the file I/O functions are supplied by you. So, + * updating a progress indicator is very simple. You keep track of + * the target files handles you have opened, along with the uncompressed + * size of the target file. When you see writes to the handle of a + * target file, you use the write count to update your status! + * Since this method is available, there is no separate callback from + * FDI just for progess indication. + */ + +#ifndef INCLUDED_FDI +#define INCLUDED_FDI 1 + +//** Specify structure packing explicitly for clients of FDI +#include <pshpack4.h> + + +/*** FDIERROR - Error codes returned in erf.erfOper field + * + * In general, FDI will only fail if one of the passed in memory or + * file I/O functions fails. Other errors are pretty unlikely, and are + * caused by corrupted cabinet files, passing in a file which is not a + * cabinet file, or cabinet files out of order. + * + * Description: Summary of error. + * Cause: List of possible causes of this error. + * Response: How client might respond to this error, or avoid it in + * the first place. + */ +typedef enum { + FDIERROR_NONE, + // Description: No error + // Cause: Function was successfull. + // Response: Keep going! + + FDIERROR_CABINET_NOT_FOUND, + // Description: Cabinet not found + // Cause: Bad file name or path passed to FDICopy(), or returned + // to fdintNEXT_CABINET. + // Response: To prevent this error, validate the existence of the + // the cabinet *before* passing the path to FDI. + + FDIERROR_NOT_A_CABINET, + // Description: Cabinet file does not have the correct format + // Cause: File passed to to FDICopy(), or returned to + // fdintNEXT_CABINET, is too small to be a cabinet file, + // or does not have the cabinet signature in its first + // four bytes. + // Response: To prevent this error, call FDIIsCabinet() to check a + // cabinet before calling FDICopy() or returning the + // cabinet path to fdintNEXT_CABINET. + + FDIERROR_UNKNOWN_CABINET_VERSION, + // Description: Cabinet file has an unknown version number. + // Cause: File passed to to FDICopy(), or returned to + // fdintNEXT_CABINET, has what looks like a cabinet file + // header, but the version of the cabinet file format + // is not one understood by this version of FDI. The + // erf.erfType field is filled in with the version number + // found in the cabinet file. + // Response: To prevent this error, call FDIIsCabinet() to check a + // cabinet before calling FDICopy() or returning the + // cabinet path to fdintNEXT_CABINET. + + FDIERROR_CORRUPT_CABINET, + // Description: Cabinet file is corrupt + // Cause: FDI returns this error any time it finds a problem + // with the logical format of a cabinet file, and any + // time one of the passed-in file I/O calls fails when + // operating on a cabinet (PFNOPEN, PFNSEEK, PFNREAD, + // or PFNCLOSE). The client can distinguish these two + // cases based upon whether the last file I/O call + // failed or not. + // Response: Assuming this is not a real corruption problem in + // a cabinet file, the file I/O functions could attempt + // to do retries on failure (for example, if there is a + // temporary network connection problem). If this does + // not work, and the file I/O call has to fail, then the + // FDI client will have to clean up and call the + // FDICopy() function again. + + FDIERROR_ALLOC_FAIL, + // Description: Could not allocate enough memory + // Cause: FDI tried to allocate memory with the PFNALLOC + // function, but it failed. + // Response: If possible, PFNALLOC should take whatever steps + // are possible to allocate the memory requested. If + // memory is not immediately available, it might post a + // dialog asking the user to free memory, for example. + // Note that the bulk of FDI's memory allocations are + // made at FDICreate() time and when the first cabinet + // file is opened during FDICopy(). + + FDIERROR_BAD_COMPR_TYPE, + // Description: Unknown compression type in a cabinet folder + // Cause: [Should never happen.] A folder in a cabinet has an + // unknown compression type. This is probably caused by + // a mismatch between the version of Diamond used to + // create the cabinet and the FDI. LIB used to read the + // cabinet. + // Response: Abort. + + FDIERROR_MDI_FAIL, + // Description: Failure decompressing data from a cabinet file + // Cause: The decompressor found an error in the data coming + // from the file cabinet. The cabinet file was corrupted. + // [11-Apr-1994 bens When checksuming is turned on, this + // error should never occur.] + // Response: Probably should abort; only other choice is to cleanup + // and call FDICopy() again, and hope there was some + // intermittent data error that will not reoccur. + + FDIERROR_TARGET_FILE, + // Description: Failure writing to target file + // Cause: FDI returns this error any time it gets an error back + // from one of the passed-in file I/O calls fails when + // writing to a file being extracted from a cabinet. + // Response: To avoid or minimize this error, the file I/O functions + // could attempt to avoid failing. A common cause might + // be disk full -- in this case, the PFNWRITE function + // could have a check for free space, and put up a dialog + // asking the user to free some disk space. + + FDIERROR_RESERVE_MISMATCH, + // Description: Cabinets in a set do not have the same RESERVE sizes + // Cause: [Should never happen]. FDI requires that the sizes of + // the per-cabinet, per-folder, and per-data block + // RESERVE sections be consistent across all the cabinet + // in a set. Diamond will only generate cabinet sets + // with these properties. + // Response: Abort. + + FDIERROR_WRONG_CABINET, + // Description: Cabinet returned on fdintNEXT_CABINET is incorrect + // Cause: NOTE: THIS ERROR IS NEVER RETURNED BY FDICopy()! + // Rather, FDICopy() keeps calling the fdintNEXT_CABINET + // callback until either the correct cabinet is specified, + // or you return ABORT. + // When FDICopy() is extracting a file that crosses a + // cabinet boundary, it calls fdintNEXT_CABINET to ask + // for the path to the next cabinet. Not being very + // trusting, FDI then checks to make sure that the + // correct continuation cabinet was supplied! It does + // this by checking the "setID" and "iCabinet" fields + // in the cabinet. When DIAMOND.EXE creates a set of + // cabinets, it constructs the "setID" using the sum + // of the bytes of all the destination file names in + // the cabinet set. FDI makes sure that the 16-bit + // setID of the continuation cabinet matches the + // cabinet file just processed. FDI then checks that + // the cabinet number (iCabinet) is one more than the + // cabinet number for the cabinet just processed. + // Response: You need code in your fdintNEXT_CABINET (see below) + // handler to do retries if you get recalled with this + // error. See the sample code (EXTRACT.C) to see how + // this should be handled. + + FDIERROR_USER_ABORT, + // Description: FDI aborted. + // Cause: An FDI callback returnd -1 (usually). + // Response: Up to client. + +} FDIERROR; + + +/*** HFDI - Handle to an FDI context + * + * FDICreate() creates this, and it must be passed to all other FDI + * functions. + */ +typedef void FAR *HFDI; /* hfdi */ + + +/*** FDICABINETINFO - Information about a cabinet + * + */ +typedef struct { + long cbCabinet; // Total length of cabinet file + USHORT cFolders; // Count of folders in cabinet + USHORT cFiles; // Count of files in cabinet + USHORT setID; // Cabinet set ID + USHORT iCabinet; // Cabinet number in set (0 based) + BOOL fReserve; // TRUE => RESERVE present in cabinet + BOOL hasprev; // TRUE => Cabinet is chained prev + BOOL hasnext; // TRUE => Cabinet is chained next +} FDICABINETINFO; /* fdici */ +typedef FDICABINETINFO FAR *PFDICABINETINFO; /* pfdici */ + + +/*** FDIDECRYPTTYPE - PFNFDIDECRYPT command types + * + */ +typedef enum { + fdidtNEW_CABINET, // New cabinet + fdidtNEW_FOLDER, // New folder + fdidtDECRYPT, // Decrypt a data block +} FDIDECRYPTTYPE; /* fdidt */ + + +/*** FDIDECRYPT - Data for PFNFDIDECRYPT function + * + */ +typedef struct { + FDIDECRYPTTYPE fdidt; // Command type (selects union below) + void FAR *pvUser; // Decryption context + union { + struct { // fdidtNEW_CABINET + void FAR *pHeaderReserve; // RESERVE section from CFHEADER + USHORT cbHeaderReserve; // Size of pHeaderReserve + USHORT setID; // Cabinet set ID + int iCabinet; // Cabinet number in set (0 based) + } cabinet; + + struct { // fdidtNEW_FOLDER + void FAR *pFolderReserve; // RESERVE section from CFFOLDER + USHORT cbFolderReserve; // Size of pFolderReserve + USHORT iFolder; // Folder number in cabinet (0 based) + } folder; + + struct { // fdidtDECRYPT + void FAR *pDataReserve; // RESERVE section from CFDATA + USHORT cbDataReserve; // Size of pDataReserve + void FAR *pbData; // Data buffer + USHORT cbData; // Size of data buffer + BOOL fSplit; // TRUE if this is a split data block + USHORT cbPartial; // 0 if this is not a split block, or + // the first piece of a split block; + // Greater than 0 if this is the + // second piece of a split block. + } decrypt; + }; +} FDIDECRYPT; /* fdid */ +typedef FDIDECRYPT FAR *PFDIDECRYPT; /* pfdid */ + + +/*** PFNFDIDECRYPT - FDI Decryption callback + * + * If this function is passed on the FDICopy() call, then FDI calls it + * at various times to update the decryption state and to decrypt FCDATA + * blocks. + * + * Common Entry Conditions: + * pfdid->fdidt - Command type + * pfdid->pvUser - pvUser value from FDICopy() call + * + * fdidtNEW_CABINET: //** Notification of a new cabinet + * Entry: + * pfdid->cabinet. + * pHeaderReserve - RESERVE section from CFHEADER + * cbHeaderReserve - Size of pHeaderReserve + * setID - Cabinet set ID + * iCabinet - Cabinet number in set (0 based) + * Exit-Success: + * returns anything but -1; + * Exit-Failure: + * returns -1; FDICopy() is aborted. + * Notes: + * (1) This call allows the decryption code to pick out any information + * from the cabinet header reserved area (placed there by DIACRYPT) + * needed to perform decryption. If there is no such information, + * this call would presumably be ignored. + * (2) This call is made very soon after fdintCABINET_INFO. + * + * fdidtNEW_FOLDER: //** Notification of a new folder + * Entry: + * pfdid->folder. + * pFolderReserve - RESERVE section from CFFOLDER + * cbFolderReserve - Size of pFolderReserve + * iFolder - Folder number in cabinet (0 based) + * Exit-Success: + * returns anything but -1; + * Exit-Failure: + * returns -1; FDICopy() is aborted. + * Notes: + * This call allows the decryption code to pick out any information + * from the folder reserved area (placed there by DIACRYPT) needed + * to perform decryption. If there is no such information, this + * call would presumably be ignored. + * + * fdidtDECRYPT: //** Decrypt a data buffer + * Entry: + * pfdid->folder. + * pDataReserve - RESERVE section for this CFDATA block + * cbDataReserve - Size of pDataReserve + * pbData - Data buffer + * cbData - Size of data buffer + * fSplit - TRUE if this is a split data block + * cbPartial - 0 if this is not a split block, or the first + * piece of a split block; Greater than 0 if + * this is the second piece of a split block. + * Exit-Success: + * returns TRUE; + * Exit-Failure: + * returns FALSE; error during decrypt + * returns -1; FDICopy() is aborted. + * Notes: + * Diamond will split CFDATA blocks across cabinet boundaries if + * necessary. To provide maximum flexibility, FDI will call the + * fdidtDECRYPT function twice on such split blocks, once when + * the first portion is read, and again when the second portion + * is read. And, of course, most data blocks will not be split. + * So, there are three cases: + * + * 1) fSplit == FALSE + * You have the entire data block, so decrypt it. + * + * 2) fSplit == TRUE, cbPartial == 0 + * This is the first portion of a split data block, so cbData + * is the size of this portion. You can either choose to decrypt + * this piece, or ignore this call and decrypt the full CFDATA + * block on the next (second) fdidtDECRYPT call. + * + * 3) fSplit == TRUE, cbPartial > 0 + * This is the second portion of a split data block (indeed, + * cbPartial will have the same value as cbData did on the + * immediately preceeding fdidtDECRYPT call!). If you decrypted + * the first portion on the first call, then you can decrypt the + * second portion now. If you ignored the first call, then you + * can decrypt the entire buffer. + * NOTE: pbData points to the second portion of the split data + * block in this case, *not* the entire data block. If + * you want to wait until the second piece to decrypt the + * *entire* block, pbData-cbPartial is the address of the + * start of the whole block, and cbData+cbPartial is its + * size. + */ +typedef int (FAR DIAMONDAPI *PFNFDIDECRYPT)(PFDIDECRYPT pfdid); /* pfnfdid */ +#define FNFDIDECRYPT(fn) int FAR DIAMONDAPI fn(PFDIDECRYPT pfdid) + + +/*** FDINOTIFICATION - Notification structure for PFNFDINOTIFY + * + * See the FDINOTIFICATIONTYPE definition for information on usage and + * meaning of these fields. + */ +typedef struct { +// long fields + long cb; + char FAR *psz1; + char FAR *psz2; + char FAR *psz3; // Points to a 256 character buffer + void FAR *pv; // Value for client + +// int fields + int hf; + +// short fields + USHORT date; + USHORT time; + USHORT attribs; + + USHORT setID; // Cabinet set ID + USHORT iCabinet; // Cabinet number (0-based) + + FDIERROR fdie; +} FDINOTIFICATION, FAR *PFDINOTIFICATION; /* fdin, pfdin */ + + +/*** FDINOTIFICATIONTYPE - FDICopy notification types + * + * The notification function for FDICopy can be called with the following + * values for the fdint parameter. In all cases, the pfdin->pv field is + * filled in with the value of the pvUser argument passed in to FDICopy(). + * + * A typical sequence of calls will be something like this: + * fdintCABINET_INFO // Info about the cabinet + * fdintPARTIAL_FILE // Only if this is not the first cabinet, and + * // one or more files were continued from the + * // previous cabinet. + * ... + * fdintPARTIAL_FILE + * fdintCOPY_FILE // The first file that starts in this cabinet + * ... + * fdintCOPY_FILE // Now let's assume you want this file... + * // PFNWRITE called multiple times to write to this file. + * fdintCLOSE_FILE_INFO // File done, set date/time/attributes + * + * fdintCOPY_FILE // Now let's assume you want this file... + * // PFNWRITE called multiple times to write to this file. + * fdintNEXT_CABINET // File was continued to next cabinet! + * fdintCABINET_INFO // Info about the new cabinet + * // PFNWRITE called multiple times to write to this file. + * fdintCLOSE_FILE_INFO // File done, set date/time/attributes + * ... + * + * fdintCABINET_INFO: + * Called exactly once for each cabinet opened by FDICopy(), including + * continuation cabinets opened due to file(s) spanning cabinet + * boundaries. Primarily intended to permit EXTRACT.EXE to + * automatically select the next cabinet in a cabinet sequence even if + * not copying files that span cabinet boundaries. + * Entry: + * pfdin->psz1 = name of next cabinet + * pfdin->psz2 = name of next disk + * pfdin->psz3 = cabinet path name + * pfdin->setID = cabinet set ID (a random 16-bit number) + * pfdin->iCabinet = Cabinet number within cabinet set (0-based) + * Exit-Success: + * Return anything but -1 + * Exit-Failure: + * Returns -1 => Abort FDICopy() call + * Notes: + * This call is made *every* time a new cabinet is examined by + * FDICopy(). So if "foo2.cab" is examined because a file is + * continued from "foo1.cab", and then you call FDICopy() again + * on "foo2.cab", you will get *two* fdintCABINET_INFO calls all + * told. + * + * fdintCOPY_FILE: + * Called for each file that *starts* in the current cabinet, giving + * the client the opportunity to request that the file be copied or + * skipped. + * Entry: + * pfdin->psz1 = file name in cabinet + * pfdin->cb = uncompressed size of file + * pfdin->date = file date + * pfdin->time = file time + * pfdin->attribs = file attributes + * Exit-Success: + * Return non-zero file handle for destination file; FDI writes + * data to this file use the PFNWRITE function supplied to FDICreate, + * and then calls fdintCLOSE_FILE_INFO to close the file and set + * the date, time, and attributes. NOTE: This file handle returned + * must also be closeable by the PFNCLOSE function supplied to + * FDICreate, since if an error occurs while writing to this handle, + * FDI will use the PFNCLOSE function to close the file so that the + * client may delete it. + * Exit-Failure: + * Returns 0 => Skip file, do not copy + * Returns -1 => Abort FDICopy() call + * + * fdintCLOSE_FILE_INFO: + * Called after all of the data has been written to a target file. + * This function must close the file and set the file date, time, + * and attributes. + * Entry: + * pfdin->psz1 = file name in cabinet + * pfdin->hf = file handle + * pfdin->date = file date + * pfdin->time = file time + * pfdin->attribs = file attributes + * pfdin->cb = Run After Extract (0 - don't run, 1 Run) + * Exit-Success: + * Returns TRUE + * Exit-Failure: + * Returns FALSE, or -1 to abort; + * + * IMPORTANT NOTE IMPORTANT: + * pfdin->cb is overloaded to no longer be the size of + * the file but to be a binary indicated run or not + * + * IMPORTANT NOTE: + * FDI assumes that the target file was closed, even if this + * callback returns failure. FDI will NOT attempt to use + * the PFNCLOSE function supplied on FDICreate() to close + * the file! + * + * fdintPARTIAL_FILE: + * Called for files at the front of the cabinet that are CONTINUED + * from a previous cabinet. This callback occurs only when FDICopy is + * started on second or subsequent cabinet in a series that has files + * continued from a previous cabinet. + * Entry: + * pfdin->psz1 = file name of file CONTINUED from a PREVIOUS cabinet + * pfdin->psz2 = name of cabinet where file starts + * pfdin->psz3 = name of disk where file starts + * Exit-Success: + * Return anything other than -1; enumeration continues + * Exit-Failure: + * Returns -1 => Abort FDICopy() call + * + * fdintNEXT_CABINET: + * This function is *only* called when fdintCOPY_FILE was told to copy + * a file in the current cabinet that is continued to a subsequent + * cabinet file. It is important that the cabinet path name (psz3) + * be validated before returning! This function should ensure that + * the cabinet exists and is readable before returning. So, this + * is the function that should, for example, issue a disk change + * prompt and make sure the cabinet file exists. + * + * When this function returns to FDI, FDI will check that the setID + * and iCabinet match the expected values for the next cabinet. + * If not, FDI will continue to call this function until the correct + * cabinet file is specified, or until this function returns -1 to + * abort the FDICopy() function. pfdin->fdie is set to + * FDIERROR_WRONG_CABINET to indicate this case. + * + * If you *haven't* ensured that the cabinet file is present and + * readable, or the cabinet file has been damaged, pfdin->fdie will + * receive other appropriate error codes: + * + * FDIERROR_CABINET_NOT_FOUND + * FDIERROR_NOT_A_CABINET + * FDIERROR_UNKNOWN_CABINET_VERSION + * FDIERROR_CORRUPT_CABINET + * FDIERROR_BAD_COMPR_TYPE + * FDIERROR_RESERVE_MISMATCH + * FDIERROR_WRONG_CABINET + * + * Entry: + * pfdin->psz1 = name of next cabinet where current file is continued + * pfdin->psz2 = name of next disk where current file is continued + * pfdin->psz3 = cabinet path name; FDI concatenates psz3 with psz1 + * to produce the fully-qualified path for the cabinet + * file. The 256-byte buffer pointed at by psz3 may + * be modified, but psz1 may not! + * pfdin->fdie = FDIERROR_WRONG_CABINET if the previous call to + * fdintNEXT_CABINET specified a cabinet file that + * did not match the setID/iCabinet that was expected. + * Exit-Success: + * Return anything but -1 + * Exit-Failure: + * Returns -1 => Abort FDICopy() call + * Notes: + * This call is almost always made when a target file is open and + * being written to, and the next cabinet is needed to get more + * data for the file. + */ +typedef enum { + fdintCABINET_INFO, // General information about cabinet + fdintPARTIAL_FILE, // First file in cabinet is continuation + fdintCOPY_FILE, // File to be copied + fdintCLOSE_FILE_INFO, // close the file, set relevant info + fdintNEXT_CABINET, // File continued to next cabinet +} FDINOTIFICATIONTYPE; /* fdint */ + +typedef int (FAR DIAMONDAPI *PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin); /* pfnfdin */ + +#define FNFDINOTIFY(fn) int FAR DIAMONDAPI fn(FDINOTIFICATIONTYPE fdint, \ + PFDINOTIFICATION pfdin) + + +/*** PFNOPEN - File I/O callbacks for FDI + * PFNREAD + * PFNWRITE + * PFNCLOSE + * PFNSEEK + * + * These are modeled after the C run-time routines _open, _read, + * _write, _close, and _lseek. The values for the PFNOPEN oflag + * and pmode calls are those defined for _open. FDI expects error + * handling to be identical to these C run-time routines. + * + * As long as you faithfully copy these aspects, you can supply + * any functions you like! + * + * + * SPECIAL NOTE FOR QUANTUM DECOMPRESSION: + * When using Quantum compression, at compress time (with Diamond) + * you specify how much memory Quantum requires at *decompress* time + * to store the decompression history buffer. This can be as large + * as *2Mb*, and in an MS-DOS environment, for example, this much + * memory may not be available (certainly not under 640K!). To permit + * large CompressionMemory settings on any machine, the Quantum + * decompressor will attempt to create a "spill file" if there is not + * sufficient memory available. + * + * For PFNOPEN, a special pszFile parameter is passed to indicate that + * a temporary "spill file" is requested. The name passed is "*", and + * you should cast the pszFile parameter to an FDISPILLFILE pointer, + * and get the requested file size. You then need to create a file + * of the specified size with read/write access, save the file name and + * handle for later use by PFNCLOSE, and then return the handle. If + * you cannot create the file of the specified size, you should return + * an error (-1). This file should be placed on a fast local hard disk, + * to maximize the speed of decompression. + * + * For PFNCLOSE, you should check the handle to see if it the spill file + * created previously by PFNOPEN (FDI will create at most one spill file + * per FDICreate() call). If it is the spill file handle, you should + * close the handle and then delete the file, using the file name you + * saved when you created the spill file in PFNOPEN. + * + * WARNING: You should never assume you know what file is being + * opened at any one point in time! FDI will usually + * stick to opening cabinet files, but it is possible + * that in a future implementation it may open temporary + * files or open cabinet files in a different order. + * + * Notes for Memory Mapped File fans: + * You can write wrapper routines to allow FDI to work on memory + * mapped files. You'll have to create your own "handle" type so that + * you can store the base memory address of the file and the current + * seek position, and then you'll allocate and fill in one of these + * structures and return a pointer to it in response to the PFNOPEN + * call and the fdintCOPY_FILE call. Your PFNREAD and PFNWRITE + * functions will do memcopy(), and update the seek position in your + * "handle" structure. PFNSEEK will just change the seek position + * in your "handle" structure. + */ +typedef int (FAR DIAMONDAPI *PFNOPEN) (char FAR *pszFile, int oflag, int pmode); +typedef UINT (FAR DIAMONDAPI *PFNREAD) (int hf, void FAR *pv, UINT cb); +typedef UINT (FAR DIAMONDAPI *PFNWRITE)(int hf, void FAR *pv, UINT cb); +typedef int (FAR DIAMONDAPI *PFNCLOSE)(int hf); +typedef long (FAR DIAMONDAPI *PFNSEEK) (int hf, long dist, int seektype); + + +#pragma pack (1) + +/** FDISPILLFILE - Pass as pszFile on PFNOPEN to create spill file + * + * ach - A two byte string to signal to PFNOPEN that a spill file is + * requested. Value is '*','\0'. + * cbFile - Required spill file size, in bytes. + */ +typedef struct { + char ach[2]; // Set to { '*', '\0' } + long cbFile; // Required spill file size +} FDISPILLFILE; /* fdisf */ +typedef FDISPILLFILE *PFDISPILLFILE; /* pfdisf */ + +#pragma pack () + + +/*** cpuType values for FDICreate() + * + * WARNING: For 16-bit Windows applications, the CPU detection may not + * correctly detect 286 CPUs. Instead, use the following code: + * + * DWORD flags; + * int cpuType; + * + * flags = GetWinFlags(); + * if (flags & WF_CPU286) + * cpuType = cpu80286; + * else + * cpuType = cpu80386; + * + * hfdi = FDICreate(....,cpuType,...); + */ +#define cpuUNKNOWN (-1) /* FDI does detection */ +#define cpu80286 (0) /* '286 opcodes only */ +#define cpu80386 (1) /* '386 opcodes used */ + + +/*** FDICreate - Create an FDI context + * + * Entry: + * pfnalloc + * pfnfree + * pfnopen + * pfnread + * pfnwrite + * pfnclose + * pfnlseek + * cpuType - Select CPU type (auto-detect, 286, or 386+) + * WARNING: Don't use auto-detect from a 16-bit Windows + * application! Use GetWinFlags()! + * NOTE: For the 32-bit FDI.LIB, this parameter is ignored! + * perf + * + * Exit-Success: + * Returns non-NULL FDI context handle. + * + * Exit-Failure: + * Returns NULL; perf filled in with error code + * + * Special notes for Quantum Decompression: + * If you have used a high setting for CompressionMemory in creating + * the cabinet file(s), then FDI will attempt to allocate a lot of + * memory (as much as 2Mb, if you specified 21 for CompressionMemory). + * Therefore, if you plan to allocate additional memory *after* the + * FDICreate() call, you should reserve some memory *prior* to calling + * FDICreate(), and then free it up afterwards (or do all your allocation + * before calling FDICreate(). + */ +HFDI FAR DIAMONDAPI FDICreate(PFNALLOC pfnalloc, + PFNFREE pfnfree, + PFNOPEN pfnopen, + PFNREAD pfnread, + PFNWRITE pfnwrite, + PFNCLOSE pfnclose, + PFNSEEK pfnseek, + int cpuType, + PERF perf); + + +/*** FDIIsCabinet - Determines if file is a cabinet, returns info if it is + * + * Entry: + * hfdi - Handle to FDI context (created by FDICreate()) + * hf - File handle suitable for PFNREAD/PFNSEEK, positioned + * at offset 0 in the file to test. + * pfdici - Buffer to receive info about cabinet if it is one. + * + * Exit-Success: + * Returns TRUE; file is a cabinet, pfdici filled in. + * + * Exit-Failure: + * Returns FALSE, file is not a cabinet; If an error occurred, + * perf (passed on FDICreate call!) filled in with error. + */ +BOOL FAR DIAMONDAPI FDIIsCabinet(HFDI hfdi, + int hf, + PFDICABINETINFO pfdici); + + +/*** FDICopy - extracts files from a cabinet + * + * Entry: + * hfdi - handle to FDI context (created by FDICreate()) + * pszCabinet - main name of cabinet file + * pszCabPath - Path to cabinet file(s) + * flags - Flags to modify behavior + * pfnfdin - Notification function + * pfnfdid - Decryption function (pass NULL if not used) + * pvUser - User specified value to pass to notification function + * + * Exit-Success: + * Returns TRUE; + * + * Exit-Failure: + * Returns FALSE, perf (passed on FDICreate call!) filled in with + * error. + * + * Notes: + * (1) If FDICopy() fails while a target file is being written out, then + * FDI will use the PFNCLOSE function to close the file handle for that + * target file that was returned from the fdintCOPY_FILE notification. + * The client application is then free to delete the target file, since + * it will not be in a valid state (since there was an error while + * writing it out). + */ +BOOL FAR DIAMONDAPI FDICopy(HFDI hfdi, + char FAR *pszCabinet, + char FAR *pszCabPath, + int flags, + PFNFDINOTIFY pfnfdin, + PFNFDIDECRYPT pfnfdid, + void FAR *pvUser); + + +/*** FDIDestroy - Destroy an FDI context + * + * Entry: + * hfdi - handle to FDI context (created by FDICreate()) + * + * Exit-Success: + * Returns TRUE; + * + * Exit-Failure: + * Returns FALSE; + */ +BOOL FAR DIAMONDAPI FDIDestroy(HFDI hfdi); + + +//** Revert to default structure packing +#include <poppack.h> + +#endif //!INCLUDED_FDI diff --git a/private/windows/diamond/chuck/makefil0 b/private/windows/diamond/chuck/makefil0 new file mode 100644 index 000000000..f90736757 --- /dev/null +++ b/private/windows/diamond/chuck/makefil0 @@ -0,0 +1,20 @@ +!INCLUDE $(NTMAKEENV)\makefile.plt + +SDKINC=$(_NTROOT)\public\sdk\inc +FCI_H = $(SDKINC)\diamondc.h +FDI_H = $(SDKINC)\diamondd.h + +all: $(FCI_H) $(FDI_H) + +$(FCI_H): fci_int.h types.h + copy types.h+fci_int.h $(FCI_H) + +$(FDI_H): fdi_int.h types.h + copy types.h+fdi_int.h $(FDI_H) + +cleanlib: + del $(FCI_H) + del $(FDI_H) + +clean: cleanlib all +
\ No newline at end of file diff --git a/private/windows/diamond/chuck/nt/fci.h b/private/windows/diamond/chuck/nt/fci.h new file mode 100644 index 000000000..35ae3fe2b --- /dev/null +++ b/private/windows/diamond/chuck/nt/fci.h @@ -0,0 +1,2 @@ +#include <diamondc.h> + diff --git a/private/windows/diamond/chuck/nt/fdi.h b/private/windows/diamond/chuck/nt/fdi.h new file mode 100644 index 000000000..ea2b440a4 --- /dev/null +++ b/private/windows/diamond/chuck/nt/fdi.h @@ -0,0 +1,2 @@ +#include <diamondd.h> + diff --git a/private/windows/diamond/chuck/resource.h b/private/windows/diamond/chuck/resource.h new file mode 100644 index 000000000..2a4bc7d45 --- /dev/null +++ b/private/windows/diamond/chuck/resource.h @@ -0,0 +1,18 @@ +//{{NO_DEPENDENCIES}} +// App Studio generated include file. +// Used by ASSERT.RC +// +#define IDD_ASSERT 101 +#define IDC_ASSERT_TEXT -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/private/windows/diamond/chuck/types.h b/private/windows/diamond/chuck/types.h new file mode 100644 index 000000000..3a1ae651a --- /dev/null +++ b/private/windows/diamond/chuck/types.h @@ -0,0 +1,192 @@ +/*** types.h - Common defines for FCI/FDI stuff -- goes into FCI/FDI.H + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 03-Mar-1993 chuckst Merged from other files + * 08-Mar-1994 bens Changed symbol to control recursive include + * 09-Mar-1994 bens Cleanups for RESERVE modifications + * 16-Mar-1994 bens Nuke padlong() + * 21-Mar-1994 bens Spruce up comments + * 22-Mar-1994 bens Add BIT16 test so we can build 16 or 32 bit! + * 26-May-1994 bens Added Quantum compression definitions + */ + +#ifndef INCLUDED_TYPES_FCI_FDI +#define INCLUDED_TYPES_FCI_FDI 1 + + +#ifdef BIT16 + +//** 16-bit build +#ifndef HUGE +#define HUGE huge +#endif + +#ifndef FAR +#define FAR far +#endif + +#else // !BIT16 + +//** Define away for 32-bit (NT/Chicago) build +#ifndef HUGE +#define HUGE +#endif + +#ifndef FAR +#define FAR +#endif + +#endif // !BIT16 + + +#ifndef DIAMONDAPI +#define DIAMONDAPI __cdecl +#endif + + +//** Specify structure packing explicitly for clients of FDI +#include <pshpack4.h> + +//** Don't redefine types defined in Win16 WINDOWS.H (_INC_WINDOWS) +// or Win32 WINDOWS.H (_WINDOWS_) +// +#if !defined(_INC_WINDOWS) && !defined(_WINDOWS_) +typedef int BOOL; /* f */ +typedef unsigned char BYTE; /* b */ +typedef unsigned int UINT; /* ui */ +typedef unsigned short USHORT; /* us */ +typedef unsigned long ULONG; /* ul */ +#endif // _INC_WINDOWS + +typedef unsigned long CHECKSUM; /* csum */ + +typedef unsigned long UOFF; /* uoff - uncompressed offset */ +typedef unsigned long COFF; /* coff - cabinet file offset */ + + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL 0 +#endif + + +/*** ERF - Error structure + * + * This structure returns error information from FCI/FDI. The caller should + * not modify this structure. + */ +typedef struct { + int erfOper; // FCI/FDI error code -- see FDIERROR_XXX + // and FCIERR_XXX equates for details. + + int erfType; // Optional error value filled in by FCI/FDI. + // For FCI, this is usually the C run-time + // *errno* value. + + BOOL fError; // TRUE => error present +} ERF; /* erf */ +typedef ERF FAR *PERF; /* perf */ + +#ifdef _DEBUG +// don't hide statics from map during debugging +#define STATIC +#else // !DEBUG +#define STATIC static +#endif // !DEBUG + +#define CB_MAX_CHUNK 32768U +#define CB_MAX_DISK 0x7ffffffL +#define CB_MAX_FILENAME 256 +#define CB_MAX_CABINET_NAME 256 +#define CB_MAX_CAB_PATH 256 +#define CB_MAX_DISK_NAME 256 + + +/*** FNALLOC - Memory Allocation + * FNFREE - Memory Free + * + * These are modeled after the C run-time routines malloc() and free() + * (16-bit clients please note -- the size is a ULONG, so you may need + * to write a wrapper routine for halloc!). FDI expects error + * handling to be identical to these C run-time routines. + * + * As long as you faithfully copy the semantics of malloc() and free(), + * you can supply any functions you like! + * + * WARNING: You should never assume anything about the sequence of + * PFNALLOC and PFNFREE calls -- incremental releases of + * Diamond/FDI may have radically different numbers of + * PFNALLOC calls and allocation sizes! + */ +typedef void HUGE * (FAR DIAMONDAPI *PFNALLOC)(ULONG cb); /* pfna */ +#define FNALLOC(fn) void HUGE * FAR DIAMONDAPI fn(ULONG cb) + +typedef void (FAR DIAMONDAPI *PFNFREE)(void HUGE *pv); /* pfnf */ +#define FNFREE(fn) void FAR DIAMONDAPI fn(void HUGE *pv) + + +/*** tcompXXX - Diamond compression types + * + * These are passed to FCIAddFile(), and are also stored in the CFFOLDER + * structures in cabinet files. + * + * NOTE: We reserve bits for the TYPE, QUANTUM_LEVEL, and QUANTUM_MEM + * to provide room for future expansion. Since this value is stored + * in the CFDATA records in the cabinet file, we don't want to + * have to change the format for existing compression configurations + * if we add new ones in the future. This will allows us to read + * old cabinet files in the future. + */ + +typedef unsigned short TCOMP; /* tcomp */ + +#define tcompMASK_TYPE 0x000F // Mask for compression type +#define tcompTYPE_NONE 0x0000 // No compression +#define tcompTYPE_MSZIP 0x0001 // MSZIP +#define tcompTYPE_QUANTUM 0x0002 // Quantum +#define tcompBAD 0x000F // Unspecified compression type + +#define tcompMASK_QUANTUM_LEVEL 0x00F0 // Mask for Quantum Compression Level +#define tcompQUANTUM_LEVEL_LO 0x0010 // Lowest Quantum Level (1) +#define tcompQUANTUM_LEVEL_HI 0x0070 // Highest Quantum Level (7) +#define tcompSHIFT_QUANTUM_LEVEL 4 // Amount to shift over to get int + +#define tcompMASK_QUANTUM_MEM 0x1F00 // Mask for Quantum Compression Memory +#define tcompQUANTUM_MEM_LO 0x0A00 // Lowest Quantum Memory (10) +#define tcompQUANTUM_MEM_HI 0x1500 // Highest Quantum Memory (21) +#define tcompSHIFT_QUANTUM_MEM 8 // Amount to shift over to get int + +#define tcompMASK_RESERVED 0xE000 // Reserved bits (high 3 bits) + + + +#define CompressionTypeFromTCOMP(tc) \ + ((tc) & tcompMASK_TYPE) + +#define CompressionLevelFromTCOMP(tc) \ + (((tc) & tcompMASK_QUANTUM_LEVEL) >> tcompSHIFT_QUANTUM_LEVEL) + +#define CompressionMemoryFromTCOMP(tc) \ + (((tc) & tcompMASK_QUANTUM_MEM) >> tcompSHIFT_QUANTUM_MEM) + +#define TCOMPfromTypeLevelMemory(t,l,m) \ + (((m) << tcompSHIFT_QUANTUM_MEM ) | \ + ((l) << tcompSHIFT_QUANTUM_LEVEL) | \ + ( t )) + + +//** Revert to default structure packing +#include <poppack.h> + +#endif // !INCLUDED_TYPES_FCI_FDI diff --git a/private/windows/diamond/command.c b/private/windows/diamond/command.c new file mode 100644 index 000000000..5ab9bedd3 --- /dev/null +++ b/private/windows/diamond/command.c @@ -0,0 +1,76 @@ +/*** command.c - Command manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 22-Apr-1994 bens Initial version + * 27-Apr-1994 bens Added DuplicateFileParm + */ + +#include <string.h> +#include <stdlib.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "command.h" + + +/*** DestroyFileParm - Function to destroy a file parameter + * + * NOTE: See command.h for entry/exit conditions. + */ +FNGLDESTROYVALUE(DestroyFileParm) +{ + PFILEPARM pfparm; + + //** Quick out if not allocated + if (pv == NULL) { + return; + } + + pfparm = pv; + AssertFparm(pfparm); + if (pfparm->pszValue != NULL) { // Free value + MemFree(pfparm->pszValue); + } + ClearAssertSignature(pfparm); + MemFree(pfparm); // Free parameter structure +} /* DestroyFileParm() */ + + +/*** DuplicateFileParm - Function to duplicate a file parameter + * + * NOTE: See command.h for entry/exit conditions. + */ +FNGLDUPLICATEVALUE(DuplicateFileParm) +{ + PFILEPARM pfparm; + PFILEPARM pfparmDup; + + pfparm = pv; + AssertFparm(pfparm); + + //** Allocate duplicate structure + if (!(pfparmDup = MemAlloc(sizeof(FILEPARM)))) { + return NULL; + } + + //** Duplicate value + if (!(pfparmDup->pszValue = MemStrDup(pfparm->pszValue))) { + MemFree(pfparmDup); + return NULL; + } + + //** Success + SetAssertSignature(pfparmDup,sigFILEPARM); + return pfparmDup; +} /* DuplicateFileParm() */ diff --git a/private/windows/diamond/command.h b/private/windows/diamond/command.h new file mode 100644 index 000000000..e98101215 --- /dev/null +++ b/private/windows/diamond/command.h @@ -0,0 +1,194 @@ +/*** command.h - Definitions for Commands derived from a directives file + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 14-Aug-1993 bens Initial version + * 20-Aug-1993 bens Added new commands + * 22-Aug-1993 bens Added ctCOMMENT, fleshed out COMMAND structure + * 08-Feb-1994 bens Add details to COMMAND structure + * 10-Feb-1994 bens Added COMMAND asserts, more complete union + * 12-Mar-1994 bens Add .Dump and .Define directives + * 22-Apr-1994 bens Add list of parameters to cmd.file + * 25-Apr-1994 bens Add customizable INF stuff + * 02-May-1994 bens Removed commands we won't implement + * 03-Jun-1994 bens Add .Option command + */ + +#ifndef INCLUDED_COMMAND +#define INCLUDED_COMMAND 1 + +#include "fileutil.h" // get cbFILE_NAME_MAX +#include "glist.h" // get generic list support +#include "variable.h" // get variable limits + +//BUGBUG 25-Apr-1994 bens Copy definition to avoid nested include hell +//#include "inf.h" // Get cbINF_LINE_MAX +#define cbINF_LINE_MAX 512 // Maximum INF line length + + +/*** COMMANDTYPE - enumeration of parsed DDF commands + * + */ +typedef enum { + ctBAD, // bad command + + ctCOMMENT, // a comment line + ctDEFINE, // Define + ctDELETE, // Delete + ctDUMP, // Dump + ctFILE, // a file specification + ctINF_BEGIN, // Begin INF lines + ctINF_END, // End INF lines + ctINF_WRITE, // Write a line to an area of the INF file + ctINF_WRITE_CAB, // InfWriteCabinet + ctINF_WRITE_DISK, // InfWriteDisk + ctOPTION, // Option + ctNEW, // New + ctREFERENCE, // an INF file reference + ctSET, // Set +} COMMANDTYPE; /* ct */ + + +/*** NEWTYPE - modifier for ctNEW (.New command) + * + */ +typedef enum { + newBAD, // bad object + + newFOLDER, // make a new folder + newCABINET, // make a new cabinet + newDISK, // make a new disk +} NEWTYPE; /* nt */ + + +/*** OPTFLAGS - modifier for ctOPTION (.Option command) + * + */ +typedef unsigned short OPTFLAGS; /* of */ +#define optEXPLICIT 0x0001 // .Option [No]Explicit + + +/*** INFAREA - modifier for ctINF_WRITE + * + */ +typedef enum { + infBAD, // bad INF area + + infDISK, // Write to disk area of INF file + infCABINET, // Write to cabinet area of INF file + infFILE, // Write to file area of INF file +} INFAREA; /* inf */ + + +#ifdef ASSERT +#define sigCOMMAND MAKESIG('C','M','D','$') // COMMAND signature +#define AssertCmd(pcmd) AssertStructure(pcmd,sigCOMMAND); +#else // !ASSERT +#define AssertCmd(pcmd) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigCOMMAND +#endif + COMMANDTYPE ct; // Command to perform + union { + struct cmdDiskLabel { // .DiskLabel + int nDisk; // Disk number (0 if not specified) + char *pszLabel; // Name printed on sticky disk label + } label; + + struct cmdDelete { // .Delete + char achVarName[cbVAR_NAME_MAX]; // Variable name + } delete; + + struct cmdFile { // {Copy File Command} + char achSrc[cbFILE_NAME_MAX]; // Source file name + char achDst[cbFILE_NAME_MAX]; // Destination (or NULL) + HGENLIST hglist; // List of /X=Y parameters + BOOL fRunFlag; // Run upon extraction + } file; + + struct cmdInfWrite { + INFAREA inf; // Area of INF to write to + char achLine[cbINF_LINE_MAX]; // Line to write to INF + } inf; + + struct cmdNew { + NEWTYPE nt; // Type of object boundary + } new; + + struct cmdOption { + OPTFLAGS of; // Option flags + OPTFLAGS ofMask; // Mask for *of* to indicate changed bits + } opt; + + struct cmdReference { // {INF File Reference} + char achDst[cbFILE_NAME_MAX]; // Destination + HGENLIST hglist; // List of /X=Y parameters + } ref; + + struct cmdSet { // .Set and .Define + char achVarName[cbVAR_NAME_MAX]; // Variable name + char achValue[cbVAR_VALUE_MAX]; // New value + } set; + + struct cmdOther { // Used by all other commands + char *psz; + } other; + }; +//BUGBUG 14-Aug-1993 bens COMMAND is incomplete +} COMMAND; /* cmd */ +typedef COMMAND *PCOMMAND; /* pcmd */ + + +#ifdef ASSERT +#define sigFILEPARM MAKESIG('F','P','A','R') // FILEPARM signature +#define AssertFparm(p) AssertStructure(p,sigFILEPARM); +#else // !ASSERT +#define AssertFparm(p) +#endif // !ASSERT + +/*** FILEPARM - Value (Y) of "/X=Y" parameter from a file copy line + * + */ +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFILEPARM +#endif + char *pszValue; // Parameter value +} FILEPARM; /* fparm */ +typedef FILEPARM *PFILEPARM; /* pfparm */ + + +/*** DestroyFileParm - Function to destroy a file parameter + * + * Entry: + * pv - pointer to a FILEPARM (may be NULL, which is a NOP) + * + * Exit: + * FILEPARM destroyed; + */ +FNGLDESTROYVALUE(DestroyFileParm); + + +/*** DuplicateFileParm - Function to duplicate a file parameter + * + * Entry: + * pv - pointer to a FILEPARM + * + * Exit-Success: + * Returns pointer to duplicated FILEPARM + * + * Exit-Failure: + * Returns NULL; + */ +FNGLDUPLICATEVALUE(DuplicateFileParm); + +#endif // !INCLUDED_COMMAND diff --git a/private/windows/diamond/crc32.c b/private/windows/diamond/crc32.c new file mode 100644 index 000000000..d5e0e8583 --- /dev/null +++ b/private/windows/diamond/crc32.c @@ -0,0 +1,71 @@ +/*** crc32.c - 32-bit CRC generator + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Mike Sliger + * + * History: + * 10-Aug-1993 bens Initial version + */ + +#include "types.h" +#include "crc32.h" + +static long crc_32_tab[] = +{ + 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, + 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, + 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, + 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, + 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, + 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, + 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, + 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, + + 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, + 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, + 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, + 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, + 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, + 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, + 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, + 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, + + 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L, + 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, + 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, + 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, + 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, + 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, + 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, + 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, + + 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L, + 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, + 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, + 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, + 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, + 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, + 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, + 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL +}; + + +/* update running CRC calculation with contents of a buffer */ + +ULONG CRC32Compute(BYTE FAR *pb,unsigned cb,ULONG crc32) +{ + //** Put CRC in form loop want it + crc32 = (-1L - crc32); + + while (cb--) + { + crc32 = crc_32_tab[(BYTE)crc32 ^ *pb++] ^ ((crc32 >> 8) & 0x00FFFFFFL); + } + + //** Put CRC in form client wants it + return (-1L - crc32); +} diff --git a/private/windows/diamond/crc32.h b/private/windows/diamond/crc32.h new file mode 100644 index 000000000..7243b93e9 --- /dev/null +++ b/private/windows/diamond/crc32.h @@ -0,0 +1,113 @@ +/*** crc32.h - 32-bit CRC generator + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Mike Sliger + * + * History: + * 10-Aug-1993 bens Copied from IMG2DMF directory. + * + * Usage: + * ULONG crc; + * //** Compute first block + * crc = CRC32Compute(pb,cb,CRC32_INITIAL_VALUE); + * //** Compute remaining blocks + * crc = CRC32Compute(pb,cb,crc); + * ... + * //** When you're done, crc has the CRC. + * + * NOTE: See example function getFileChecksum() below that + * computes the checksum of a file! + */ + +//** Must use this as initial value for CRC +#define CRC32_INITIAL_VALUE 0L + + +/*** CRC32Compute - Compute 32-bit + * + * Entry: + * pb - Pointer to buffer to computer CRC on + * cb - Count of bytes in buffer to CRC + * crc32 - Result from previous CRC32Compute call (on first call + * to CRC32Compute, must be CRC32_INITIAL_VALUE!!!!). + * + * Exit: + * Returns updated CRC value. + */ +ULONG CRC32Compute(BYTE *pb,unsigned cb,ULONG crc32); + + +//** Include nice sample -- don't compile it +// +#ifdef HERE_IS_A_SAMPLE + +/*** getFileChecksum - Compute file checksum + * + * Entry: + * pszFile - Filespec + * pchecksum - Receives 32-bit checksum of file + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, *pchecksum filled in. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL getFileChecksum(char *pszFile, ULONG *pchecksum, PERROR perr) +{ +#define cbCSUM_BUFFER 4096 // File buffer size + int cb; // Amount of data in read buffer + ULONG csum=CRC32_INITIAL_VALUE; // Initialize CRC + char *pb=NULL; // Read buffer + int hf=-1; // File handle + int rc; + BOOL result=FALSE; // Assume failure + + //** Initialize returned checksum (assume failure) + *pchecksum = csum; + + //** Allocate file buffer + if (!(pb = MemAlloc(cbCSUM_BUFFER))) { + ErrSet(perr,pszDIAERR_NO_MEMORY_CRC,"%s",pszFile); + return FALSE; + } + + //** Open file + hf = _open(pszFile,_O_RDONLY | _O_BINARY,&rc); + if (hf == -1) { + ErrSet(perr,pszDIAERR_OPEN_FAILED,"%s",pszFile); + goto Exit; + } + + //** Compute checksum + while (_eof(hf) == 0) { + cb = _read(hf,pb,cbCSUM_BUFFER); + if (cb == -1) { + ErrSet(perr,pszDIAERR_READ_FAIL_CRC,"%s",pszFile); + goto Exit; + } + if (cb != 0) { + csum = CRC32Compute(pb,cb,csum); // Accumulate CRC + } + } + + //** Success + result = TRUE; + *pchecksum = csum; // Store checksum for caller + +Exit: + if (hf != -1) { + _close(hf); + } + if (pb != NULL) { + MemFree(pb); + } + return result; +} /* getFileChecksum() */ + +#endif // HERE_IS_A_SAMPLE diff --git a/private/windows/diamond/ddump.c b/private/windows/diamond/ddump.c new file mode 100644 index 000000000..ff6a8bc95 --- /dev/null +++ b/private/windows/diamond/ddump.c @@ -0,0 +1,787 @@ +/*** ddump.c - Diamond cabinet file dumper + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 16-Mar-1994 bens Initial version (started with extract.c) + * 28-Mar-1994 bens Update for version 1.03. + * 18-May-1994 bens Correct order of cabinet file name, disk name + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <malloc.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <errno.h> +#include <direct.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "filelist.h" +#include "fileutil.h" + +#include "chuck\types.h" +#include "chuck\cabinet.h" + +#include "ddump.msg" + +//** Constants + +#define cbMAX_LINE 256 // Maximum output line length + + +//** Types + +typedef enum { + actBAD, // Invalid action + actHELP, // Show help + actFILE, // Extract from single file cabinet(s) + actCABINET, // Extract from multiple-file cabinet(s) +} ACTION; /* act */ + + +#ifdef ASSERT +#define sigSESSION MAKESIG('S','E','S','S') // SESSION signature +#define AssertSess(psess) AssertStructure(psess,sigSESSION); +#else // !ASSERT +#define AssertSess(psess) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigSESSION +#endif + ACTION act; // Action to perform + HFILELIST hflist; // List of files specified on cmd line + BOOL fNoLineFeed; // TRUE if last printf did not have \n + BOOL fVerbose; // TRUE ==> Dump everything + long cFiles; // Total files processed + PERROR perr; // Pass through FDI + char achMsg[cbMAX_LINE*2]; // Message formatting buffer + char achFile[cbFILE_NAME_MAX]; // Current filename being extracted + + char achPrevDisk[255]; + char achPrevCabFile[255]; + char achNextDisk[255]; + char achNextCabFile[255]; + + CFHEADER cfheader; + CFRESERVE cfreserve; + char *pbCFHeaderReserve; // Reserved data for cabinet + CFFOLDER *pcffolder; // CFFOLDER + reserved buffer + CFDATA *pcfdata; // CFDATA + reserved buffer + + UINT cbCFHeaderReserve; // Size of CFHEADER RESERVE + UINT cbCFFolderPlusReserve; // Size of CFFOLDER + folder RESERVE + UINT cbCFDataPlusReserve; // Size of CFDATA + data RESERVE + +} SESSION; /* sess */ +typedef SESSION *PSESSION; /* psess */ + + +//** Function Prototypes + +FNASSERTFAILURE(fnafReport); + +HFILESPEC addFileSpec(PSESSION psess,char *pszArg,PERROR perr); +BOOL doCabinet(PSESSION psess,PERROR perr); +BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr); +void printError(PSESSION psess,PERROR perr); +void pszFromMSDOSTime(char *psz,int cb,WORD date,WORD time); + +BOOL doCFHeader(PSESSION psess,int fh,char *pszFile,PERROR perr); +BOOL doCFFolder(PSESSION psess,int fh,int iFolder,PERROR perr); +BOOL doCFFile(PSESSION psess,int fh,int iFile,PERROR perr); +ULONG doCFData(PSESSION psess,int fh,int iData,ULONG uoffFolder,PERROR perr); + +BOOL readPSZ(int fh, char *pb, int cb); + + +//** Functions + +/*** main - DDUMP main program + * + * NOTE: We're sloppy, and don't free resources allocated by + * functions we call, on the assumption that program exit + * will clean up memory and file handles for us. + */ +int __cdecl main(int cArg, char *apszArg[]) +{ + char ach[cbMSG_MAX]; + ERROR err; + PSESSION psess; + + AssertRegisterFunc(fnafReport); // Register assertion reporter + ErrClear(&err); // No error + err.pszFile = NULL; // No file being processed, yet + + //** Initialize session + psess = MemAlloc(sizeof(SESSION)); + if (!psess) { + ErrSet(&err,pszDDERR_NO_SESSION); + printError(psess,&err); + exit(1); + } + SetAssertSignature((psess),sigSESSION); + psess->hflist = NULL; + psess->fNoLineFeed = FALSE; // No print status, yet + psess->fVerbose = FALSE; // Default to non-verbose output + psess->cFiles = 0; // No files, yet + psess->pbCFHeaderReserve = NULL; // No per-cabinet reserved data, yet + psess->pcffolder = NULL; // No buffer for CFFOLDER, yet + psess->pcfdata = NULL; // No buffer for CFDATA, yet + + //** Print Extract banner + MsgSet(ach,pszBANNER,"%s",pszDDUMP_VERSION); + printf(ach); + + //** Parse command line + if (!parseCommandLine(psess,cArg,apszArg,&err)) { + printError(psess,&err); + return 1; + } + + //** Quick out if command line help is requested + if (psess->act == actHELP) { // Do help if any args, for now + printf("\n"); // Separate banner from help + printf(pszCMD_LINE_HELP); + return 0; + } + + //** Have some work to do -- go do it + if (!doCabinet(psess,&err)) { + printError(psess,&err); + return 1; + } + + //** Free resources + AssertSess(psess); + ClearAssertSignature((psess)); + MemFree(psess); + + //** Success + return 0; +} /* main */ + + +/*** doCabinet - Dump contents of cabinet + * + * Entry: + * psess - Description of operation to perform + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; directory displayed + * + * Exit-Failure: + * Returns FALSE; perr filled in with details. + */ +BOOL doCabinet(PSESSION psess, PERROR perr) +{ + UINT cCFData; + int fh; // File handle of cabinet + HFILESPEC hfspec; + UINT i; + ULONG off; + ULONG offCFDataForFirstFolder; + char *pszCabinet; // Cabinet filespec + ULONG uoff; + ULONG uoffFolder; + + //** Process a cabinet + hfspec = FLFirstFile(psess->hflist); + Assert(hfspec != NULL); // Must have at least one file + pszCabinet = FLGetSource(hfspec); + Assert(pszCabinet!=NULL); + Assert(*pszCabinet); + + //** Open cabinet file + if (-1 == (fh = _open(pszCabinet,_O_BINARY | _O_RDONLY))) { + ErrSet(perr,pszDDERR_OPEN_FAILED,"%s",pszCabinet); + return FALSE; + } + + printf("Cabinet File: %s\n",pszCabinet); + + //** Do header + if (!doCFHeader(psess,fh,pszCabinet,perr)) { + return FALSE; + } + + //** Do folders + cCFData = 0; + for (i=0; i<psess->cfheader.cFolders; i++) { + if (!doCFFolder(psess,fh,i,perr)) { + return FALSE; + } + cCFData += psess->pcffolder->cCFData; // Count total CFData's + if (i == 0) { + offCFDataForFirstFolder = psess->pcffolder->coffCabStart; + } + } + + //** Make sure CFFILEs start where header said they did + off = _lseek(fh,0,SEEK_CUR); + if (psess->cfheader.coffFiles != off) { + printf("ERROR: Header says CFFILES start at %ld, really start at %ld!\n", + psess->cfheader.coffFiles, off); + } + + //** Do files + for (i=0; i<psess->cfheader.cFiles; i++) { + if (!doCFFile(psess,fh,i,perr)) { + return FALSE; + } + } + + //** Make sure CFDATAs start where first CFFOLDER said they did + off = _lseek(fh,0,SEEK_CUR); + if (offCFDataForFirstFolder != off) { + printf("ERROR: CFFOLDER says CFDATA start at %ld, really start at %ld!\n", + offCFDataForFirstFolder, off); + } + + //** Only traverse through CFDATAs if requested (speeds up dumping on + // floppy disks, since we don't have to read the entire floppy! + if (psess->fVerbose) { + //** Do data blocks + uoffFolder = 0; // Track uncompressed offset + for (i=0; i<cCFData; i++) { + if (-1 == (uoff = doCFData(psess,fh,i,uoffFolder,perr))) { + return FALSE; + } + uoffFolder += uoff; // Advance uncompressed offset + } + + //** Make sure cabinet file is expected length + off = _lseek(fh,0,SEEK_CUR); + if (psess->cfheader.cbCabinet != (long)off) { + printf("ERROR: Header says cabinet is %ld bytes, really is %ld!\n", + psess->cfheader.cbCabinet, off); + } + } + + //** Success + return TRUE; +} /* doCabinet() */ + + +/*** doCFHeader - display CFHEADER information + * + */ +BOOL doCFHeader(PSESSION psess, int fh, char *pszFile, PERROR perr) +{ + BOOL fPrev=FALSE; + BOOL fNext=FALSE; + BOOL fReserve=FALSE; + + //** Read CFHEADER structure + if (sizeof(CFHEADER) != _read(fh,&psess->cfheader,sizeof(CFHEADER))) { + ErrSet(perr,pszDDERR_NOT_A_CABINET,"%s",pszFile); + return FALSE; + } + + //** Make sure this is a cabinet file + if (psess->cfheader.sig != sigCFHEADER) { + ErrSet(perr,pszDDERR_NOT_A_CABINET,"%s",pszFile); + return FALSE; // Signature does not match + } + + //** Make sure we know this version number + if (psess->cfheader.version != verCF) { + ErrSet(perr,pszDDERR_UNKNOWN_CABINET_VERSION,"%s%d", + pszFile,psess->cfheader.version); + return FALSE; + } + + + + psess->achMsg[0] = '\0'; + if (psess->cfheader.flags & cfhdrPREV_CABINET) { + strcat(psess->achMsg,"Previous "); + fPrev = TRUE; + } + + if (psess->cfheader.flags & cfhdrNEXT_CABINET) { + strcat(psess->achMsg,"Next "); + fNext = TRUE; + } + + if (psess->cfheader.flags & cfhdrRESERVE_PRESENT) { + strcat(psess->achMsg,"Reserve "); + fReserve = TRUE; + } + + printf("sig: %08lx\n" ,psess->cfheader.sig); + printf("csumHeader: %08lx\n" ,psess->cfheader.csumHeader); + printf("cbCabinet: %8ld\n" ,psess->cfheader.cbCabinet); + printf("csumFolders: %08lx\n" ,psess->cfheader.csumFolders); + printf("coffFiles: %8ld\n" ,psess->cfheader.coffFiles); + printf("csumFiles: %08lx\n" ,psess->cfheader.csumFiles); + printf("version: %04x\n" ,psess->cfheader.version); + printf("cFolders: %8d\n" ,psess->cfheader.cFolders); + printf("cFiles: %8d\n" ,psess->cfheader.cFiles); + printf("flags: %04x (%s)\n",psess->cfheader.flags,psess->achMsg); + printf("setID: %04x\n" ,psess->cfheader.setID); + printf("iCabinet: %05d\n" ,psess->cfheader.iCabinet); + + //** Assume no CFRESERVE + psess->cbCFHeaderReserve = 0; + psess->cbCFFolderPlusReserve = sizeof(CFFOLDER); + psess->cbCFDataPlusReserve = sizeof(CFDATA); + + //** Read CFRESERVE, if present + if (fReserve) { + //** Read CFRESERVE structure + if (sizeof(CFRESERVE) != _read(fh,&psess->cfreserve,sizeof(CFRESERVE))) { + return FALSE; + } + + if (psess->pbCFHeaderReserve) { + MemFree(psess->pbCFHeaderReserve); + psess->pbCFHeaderReserve = NULL; + } + if (!(psess->pbCFHeaderReserve = MemAlloc(psess->cfreserve.cbCFHeader))) { + return FALSE; + } + + psess->cbCFHeaderReserve = psess->cfreserve.cbCFHeader; + psess->cbCFFolderPlusReserve += psess->cfreserve.cbCFFolder; + psess->cbCFDataPlusReserve += psess->cfreserve.cbCFData; + + printf(" cbCFHeader: %5d\n", psess->cfreserve.cbCFHeader); + printf(" cbCFFolder: %5d\n", psess->cfreserve.cbCFFolder); + printf(" cbCFData: %5d\n", psess->cfreserve.cbCFData); + + //** Read reserved section + if (psess->cbCFHeaderReserve != (UINT)_read(fh, + psess->pbCFHeaderReserve, + psess->cbCFHeaderReserve)) { + return FALSE; + } +//BUGBUG 17-Mar-1994 bens Print out reserved bytes with hex dump? + } + + //** Allocate CFFOLDER and CFDATA buffers + if (psess->pcffolder) { + MemFree(psess->pcffolder); + psess->pcffolder = NULL; + } + if (!(psess->pcffolder = MemAlloc(psess->cbCFFolderPlusReserve))) { + return FALSE; + } + + if (psess->pcfdata) { + MemFree(psess->pcfdata); + psess->pcfdata = NULL; + } + if (!(psess->pcfdata = MemAlloc(psess->cbCFDataPlusReserve))) { + return FALSE; + } + + //** Read prev/next disk/cab strings + psess->achPrevCabFile[0] = '\0'; + psess->achPrevDisk[0] = '\0'; + psess->achNextCabFile[0] = '\0'; + psess->achNextDisk[0] = '\0'; + + if (fPrev) { + if (!readPSZ(fh,psess->achPrevCabFile,sizeof(psess->achPrevCabFile))) { + return FALSE; + } + if (!readPSZ(fh,psess->achPrevDisk,sizeof(psess->achPrevDisk))) { + return FALSE; + } + printf("PrevCabFile: %s\n",psess->achPrevCabFile); + printf("PrevDisk: %s\n",psess->achPrevDisk); + } + + if (fNext) { + if (!readPSZ(fh,psess->achNextCabFile,sizeof(psess->achNextCabFile))) { + return FALSE; + } + if (!readPSZ(fh,psess->achNextDisk,sizeof(psess->achNextDisk))) { + return FALSE; + } + printf("NextCabFile: %s\n",psess->achNextCabFile); + printf("NextDisk: '%s'\n",psess->achNextDisk); + } + + //** Blank line to separate header from first folder + printf("\n"); + return TRUE; +} + + +/*** doCFFolderr - display CFFOLDER information + * + */ +BOOL doCFFolder(PSESSION psess, int fh, int iFolder, PERROR perr) +{ + char ach[256]; + static BOOL fFirstTime=TRUE; + char *pszCompression; + + //** Read CFFOLDER and reserved data + if (psess->cbCFFolderPlusReserve != (UINT)_read(fh, + psess->pcffolder, + psess->cbCFFolderPlusReserve)) { + return FALSE; + } + + switch (CompressionTypeFromTCOMP(psess->pcffolder->typeCompress)) { + case tcompTYPE_NONE: + pszCompression = "None"; + break; + + case tcompTYPE_MSZIP: + pszCompression = "MSZIP"; + break; + + case tcompTYPE_QUANTUM: + sprintf(ach,"Quantum Level=%d Memory=%d", + CompressionLevelFromTCOMP(psess->pcffolder->typeCompress), + CompressionMemoryFromTCOMP(psess->pcffolder->typeCompress)); + pszCompression = ach; + break; + + default: + sprintf(psess->achMsg,"Unknown compression: %04x", + psess->pcffolder->typeCompress); + pszCompression = psess->achMsg; + break; + } + + if (fFirstTime) { + printf("iFolder offCabinet cData Compression\n"); + fFirstTime = FALSE; + } + printf(" %3d %8ld %5d %s\n", + iFolder, + psess->pcffolder->coffCabStart, + psess->pcffolder->cCFData, + pszCompression); + +//BUGBUG 17-Mar-1994 bens Dump reserved data in hex dump form? + return TRUE; +} + + +/*** doCFFile - display CFFILE information + * + */ +BOOL doCFFile(PSESSION psess, int fh, int iFile, PERROR perr) +{ + CFFILE cffile; + static BOOL fFirstTime=TRUE; + + psess->cFiles++; // Count file + + //** Read CFFILE structure + if (sizeof(CFFILE) != _read(fh,&cffile,sizeof(cffile))) { + return FALSE; + } + + if (!readPSZ(fh,psess->achFile,sizeof(psess->achFile))) { + return FALSE; + } + + //** Format date and time + pszFromMSDOSTime(psess->achMsg, + sizeof(psess->achMsg), + cffile.date, + cffile.time); + + if (fFirstTime) { + printf("\n"); + printf("iFile cbFile offFolder iFolder attr date time name\n"); + fFirstTime = FALSE; + } + printf("%5d %8ld %9ld %7d %04x %s %s\n", + iFile, + cffile.cbFile, + cffile.uoffFolderStart, + cffile.iFolder, + cffile.attribs, + psess->achMsg, + psess->achFile + ); + + return TRUE; +} + + +/*** doCFData - display CFDATA information + * + */ +ULONG doCFData(PSESSION psess, int fh, int iData, ULONG uoffFolder, PERROR perr) +{ + static BOOL fFirstTime=TRUE; + + //** Read CFFOLDER and reserved data + if (psess->cbCFDataPlusReserve != (UINT)_read(fh, + psess->pcfdata, + psess->cbCFDataPlusReserve)) { + return FALSE; + } + + if (fFirstTime) { + printf("\n"); + printf("iData csumData offFolder cbData cbUncomp\n"); + fFirstTime = FALSE; + } + printf("%5d %08lx %9ld %6u %6u\n", + iData, + psess->pcfdata->csum, + uoffFolder, + psess->pcfdata->cbData, + psess->pcfdata->cbUncomp); + + //** Skip over actual data + _lseek(fh,psess->pcfdata->cbData,SEEK_CUR); + + return (ULONG)psess->pcfdata->cbUncomp; +} /* doCFData() */ + + +/*** readPSZ - read ASCIIZ string from file + * + */ +BOOL readPSZ(int fh, char *pb, int cb) +{ + char chLast; + int cbValue; + int cbRead; + long pos; + + pos = _lseek(fh,0,SEEK_CUR); // Save current position + + //** Read in enough to get longest possible value + cbRead = _read(fh,pb,cb); +//BUGBUG 17-Mar-1994 bens Need to check for error/eof from read + chLast = pb[cb-1]; // Save last character + pb[cb-1] = '\0'; // Ensure terminated + cbValue = strlen(pb); // Get string length + if ( ((cbValue+1) >= cb) && (chLast != '\0')) { + //** String filled up buffer and was not null terminated in + // file, so something must be wrong. + return FALSE; + } + + _lseek(fh,pos+cbValue+1,SEEK_SET); // Position to just past string + return TRUE; +} + + +/*** parseCommandLine - Parse the command line arguments + * + * Entry: + * cArg - Count of arguments, including program name + * apszArg - Array of argument strings + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess filled in. + * + * Exit-Failure: + * Returns actBAD; perr filled in with error. + */ +BOOL parseCommandLine(PSESSION psess, int cArg, char *apszArg[], PERROR perr) +{ + int cFile=0; // Count of non-directive file names seen + char ch; // Switch value + int i; + + AssertSess(psess); + psess->act = actBAD; // We don't know what we are doing, yet + + //** Parse args, skipping program name + for (i=1; i<cArg; i++) { + if ((apszArg[i][0] == chSWITCH1) || + (apszArg[i][0] == chSWITCH2) ) { + + ch = toupper(apszArg[i][1]); // Switch value + switch (ch) { + case chSWITCH_VERBOSE: + if (apszArg[i][2] != '\0') { + ErrSet(perr,pszDDERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + } + psess->fVerbose = TRUE; // Show everything + break; + + case chSWITCH_HELP: + if (apszArg[i][2] != '\0') { + ErrSet(perr,pszDDERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + } + psess->act = actHELP; // Show help + return TRUE; + + default: + ErrSet(perr,pszDDERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + break; + } + } + else { + //** We have a file name; add it to our list + if (addFileSpec(psess,apszArg[i],perr) == NULL) { + ErrSet(perr,pszDDERR_COULD_NOT_ADD_FILE,"%s",apszArg[i]); + return FALSE; + } + cFile++; // Count files + } + } + + //** Make sure we got at least one filespec + if (cFile == 0) { + psess->act = actHELP; // Show help + } + + //** Success + return TRUE; +} + + +/*** addFileSpec - Add filename to session list + * + * Entry: + * psess - Session to update + * pszArg - File name to add + * perr - ERROR structure + * + * Exit-Success: + * Returns HFILESPEC; psess updated. + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr) +{ + HFILESPEC hfspec; + + AssertSess(psess); + //** Make sure a list exists + if (psess->hflist == NULL) { + if (!(psess->hflist = FLCreateList(perr))) { + return FALSE; + } + } + + //** Add file to list + if (!(hfspec = FLAddFile(psess->hflist, pszArg, NULL, perr))) { + return NULL; + } + + //** Success + return hfspec; +} /* addFileSpec() */ + + +#ifdef ASSERT +/*** fnafReport - Report assertion failure + * + * NOTE: See asrt.h for entry/exit conditions. + */ +FNASSERTFAILURE(fnafReport) +{ + printf("\n%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg); + exit(1); +} +#endif // ASSERT + + +/*** printError - Display error on stdout + * + * Entry + * perr - ERROR structure to print + * + * Exit-Success + * Writes error message to stdout. + */ +void printError(PSESSION psess, PERROR perr) +{ + //** Make sure error starts on a new line + if (psess->fNoLineFeed) { + printf("\n"); + psess->fNoLineFeed = FALSE; + } + + //** General error + printf("%s: %s\n",pszDDERR_ERROR,perr->ach); +} + + +/*** pszFromMSDOSTime - Convert MS-DOS file date/time to string + * + * Entry: + * psz - Buffer to receive formatted date/time + * cb - Length of psz buffer + * date - MS-DOS FAT file system date format (see below) + * time - MS-DOS FAT file system time format (see below) + * + * Exit: + * *ptm filled in + * + * NOTE: This is the interpretation of the MS-DOS date/time values: + * + * Time Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Number of two-second increments (0 - 29) + * 5 - 10 6 Minutes (0 - 59) + * 11 - 15 5 Hours (0 - 23) + * + * Date Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Day (1 - 31) + * 5 - 8 4 Month (1 - 12) + * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14) + */ +void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time) +{ + int sec; + int min; + int hour; + int day; + int month; + int year; + char *pszAMPM; // AM/PM string + + sec = (time & 0x1f) << 1; + min = (time >> 5) & 0x3f; + hour = (time >> 11) & 0x1f; + + //** Get am/pm extension, and map 0 to 12 + if (hour >= 12) { + pszAMPM = pszDD_TIME_PM; + hour -= 12; + } + else { + pszAMPM = pszDD_TIME_AM; + } + if (hour == 0) { + hour = 12; + } + + day = (date & 0x1f); + month = (date >> 5) & 0x0f; + year = ((date >> 9) & 0x7f) + 1980; + + MsgSet(psz,pszDD_DATE_TIME, "%02d%02d%02d%2d%02d%02d%s", + month, day, year, hour, min, sec, pszAMPM); +} /* pszFromMSDOSTime() */ diff --git a/private/windows/diamond/ddump.msg b/private/windows/diamond/ddump.msg new file mode 100644 index 000000000..883b13b2f --- /dev/null +++ b/private/windows/diamond/ddump.msg @@ -0,0 +1,57 @@ +/*** ddump.msg - DDUMP.EXE displayable strings + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * History: + * 16-Mar-1994 bens Initial version + * 18-Apr-1994 bens Add bitness of build to banner; make sure file + * looks like a cabinet. + * 27-May-1994 bens Quantum support + */ + +//** Command Line Switches + +#ifdef BIT16 +#define pszDDUMP_VERSION "(16) 1.00.0540 (02/01/96)" // For %1 in pszBANNER +#else +#define pszDDUMP_VERSION "(32) 1.00.0540 (02/01/96)" // For %1 in pszBANNER +#endif + +#define pszBANNER \ + "Microsoft (R) Diamond Cabinet Dumper - Version %1\n" \ + "Copyright (c) Microsoft Corp 1994-1996. All rights reserved.\n" + +#define pszCMD_LINE_HELP \ + "DDUMP [/V] cabinet\n" \ + "\n" \ + " cabinet - Cabinet file to dump.\n" \ + " /V - Verbose output (show CFDATA records).\n" \ + +#define chSWITCH1 '/' +#define chSWITCH2 '-' + +#define chSWITCH_VERBOSE 'V' // Show CDDATA records + +#define chSWITCH_HELP '?' + + +//** Status messages + +#define pszDD_DATE_TIME "%1-%2-%3 %4:%5:%6%7" +#define pszDD_TIME_AM "a" +#define pszDD_TIME_PM "p" + + +//** Error messages + +#define pszDDERR_ERROR "ERROR" +#define pszDDERR_BAD_SWITCH "Invalid switch: %1" +#define pszDDERR_BAD_PARAMETER "Invalid parameter: %1" +#define pszDDERR_SWITCH_NOT_EXPECTED "Switch not expected: %1" +#define pszDDERR_NO_SESSION "Could not allocate SESSION" +#define pszDDERR_COULD_NOT_ADD_FILE "Could not add filespec: %1" +#define pszDDERR_OPEN_FAILED "Could not open cabinet: %1" +#define pszDDERR_NOT_A_CABINET "%1 is not a cabinet file" +#define pszDDERR_UNKNOWN_CABINET_VERSION "Unknown cabinet version (%2) in %1" diff --git a/private/windows/diamond/dfparse.c b/private/windows/diamond/dfparse.c new file mode 100644 index 000000000..8d607b1a8 --- /dev/null +++ b/private/windows/diamond/dfparse.c @@ -0,0 +1,2140 @@ +/*** dfparse.c - Directives File parser + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 12-Aug-1993 bens Added more entries to aiv[] + * 14-Aug-1993 bens Start working on parser proper + * 20-Aug-1993 bens Add more standard variables + * 22-Aug-1993 bens Do variable substitution in directive lines + * 11-Feb-1994 bens Start parsing individual commands (.Set) + * 16-Feb-1994 bens Handle ClusterSize; add DiskLabelTemplate + * 17-Feb-1994 bens Added 360K/720K disk parms; fixed validaters + * 12-Mar-1994 bens Add .Dump and .Define directives + * 25-Apr-1994 bens Add customizable INF stuff + * 26-May-1994 bens Add CompressionXxxx variables + * 03-Jun-1994 bens Implement .Define, .Option, .Dump + * 11-Jul-1994 bens Check for blank/comments lines *after* variable + * substitution! + * 27-Jul-1994 bens Allow leading blanks in .InfWrite[Xxx] + * 28-Mar-1995 jeffwe Add ChecksumWidth variable + * + * Exported Functions: + * DFPInit - Initialize directive file parser + * DFPParse - Parse a directive file + * DFPParseVarAssignment - Parse var=value string + */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "variable.h" +#include "dfparse.h" +#include "inf.h" +#include "mem.h" +#include "misc.h" + +#include "dfparse.msg" +#include "diamond.msg" + + +FNVCVALIDATE(fnvcvBool); +FNVCVALIDATE(fnvcvCabName); +FNVCVALIDATE(fnvcvChecksumWidth); +FNVCVALIDATE(fnvcvClusterSize); +FNVCVALIDATE(fnvcvCompType); +FNVCVALIDATE(fnvcvCompLevel); +FNVCVALIDATE(fnvcvCompMemory); +FNVCVALIDATE(fnvcvDateFmt); +FNVCVALIDATE(fnvcvDirDest); +FNVCVALIDATE(fnvcvDirSrc); +FNVCVALIDATE(fnvcvFile); +FNVCVALIDATE(fnvcvFileChar); +FNVCVALIDATE(fnvcvLong); +FNVCVALIDATE(fnvcvMaxDiskFileCount); +FNVCVALIDATE(fnvcvMaxDiskSize); +FNVCVALIDATE(fnvcvSectionOrder); +FNVCVALIDATE(fnvcvWildFile); +FNVCVALIDATE(fnvcvWildPath); + + +COMMANDTYPE ctFromCommandString(char *pszCmd, PERROR perr); +BOOL getCmdFromLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr); +BOOL getCommand(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr); +char *getQuotedString(char *pszDst, + int cbDst, + char *pszSrc, + char *pszDelim, + char *pszFieldName, + PERROR perr); +int IMDSfromPSZ(char *pszValue); +BOOL parseFileLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr); +char *parseParameterList(HGENLIST *phglist, char *pch, PERROR perr, BOOL *runflg); +BOOL parseReferenceLine(PCOMMAND pcmd,PSESSION psess,char *pszLine,PERROR perr); +BOOL parseSetCommand(PCOMMAND pcmd, PSESSION psess, char *pszArg, PERROR perr); +BOOL processLineWithQuotes(char *pszDst, + int cbDst, + char *pszSrc, + char *pszDelim, + char *pszFieldName, + PERROR perr); +long roundUpToPowerOfTwo(long x); +BOOL substituteVariables(char *pszDst, + int cbDst, + char *pszSrc, + HVARLIST hvlist, + PERROR perr); + + +//** aiv - list of predefined variables + +typedef struct { + char *pszName; // Variable name + char *pszValue; // Default value + VARTYPE vtype; // Variable type + VARFLAGS vfl; // Special flags + PFNVCVALIDATE pfnvcv; // Validation function +} INITVAR; /* iv */ + +//** NOTE: The vflCOPY flag is used for variables whose *last* value must +//** be carried over to the *Pass 2* variable list. At present, the +//** variables that control the INF file headers and formats +//** require this property! + +STATIC INITVAR aiv[] = { +{pszVAR_CABINET ,pszDEF_CABINET ,vtypeBOOL,vflPERM ,fnvcvBool }, +{pszVAR_CABINET_FILE_COUNT_THRESHOLD,pszDEF_CABINET_FILE_COUNT_THRESHOLD,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_CAB_NAME ,pszDEF_CAB_NAME ,vtypeSTR ,vflPERM ,fnvcvWildPath}, +{pszVAR_CHECKSUM_WIDTH ,pszDEF_CHECKSUM_WIDTH ,vtypeLONG,vflPERM ,fnvcvChecksumWidth}, +{pszVAR_CLUSTER_SIZE ,pszDEF_CLUSTER_SIZE ,vtypeLONG,vflPERM ,fnvcvClusterSize}, +{pszVAR_COMPRESS ,pszDEF_COMPRESS ,vtypeBOOL,vflPERM ,fnvcvBool }, +{pszVAR_COMP_FILE_EXT_CHAR ,pszDEF_COMP_FILE_EXT_CHAR ,vtypeCHAR,vflPERM ,fnvcvFileChar}, +{pszVAR_COMPRESSION_TYPE ,pszDEF_COMPRESSION_TYPE ,vtypeSTR ,vflPERM ,fnvcvCompType}, +{pszVAR_COMPRESSION_LEVEL ,pszDEF_COMPRESSION_LEVEL ,vtypeLONG,vflPERM ,fnvcvCompLevel}, +{pszVAR_COMPRESSION_MEMORY ,pszDEF_COMPRESSION_MEMORY ,vtypeLONG,vflPERM ,fnvcvCompMemory}, +{pszVAR_DIR_DEST ,pszDEF_DIR_DEST ,vtypeSTR ,vflPERM ,fnvcvDirDest }, +{pszVAR_DISK_DIR_NAME ,pszDEF_DISK_DIR_NAME ,vtypeSTR ,vflPERM ,fnvcvWildPath}, +{pszVAR_DISK_LABEL_NAME ,pszDEF_DISK_LABEL_NAME ,vtypeSTR ,vflPERM ,NULL }, +{pszVAR_DO_NOT_COPY_FILES ,pszDEF_DO_NOT_COPY_FILES ,vtypeBOOL,vflPERM ,fnvcvBool }, +{pszVAR_FOLDER_FILE_COUNT_THRESHOLD ,pszDEF_FOLDER_FILE_COUNT_THRESHOLD ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_FOLDER_SIZE_THRESHOLD ,pszDEF_FOLDER_SIZE_THRESHOLD ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_GENERATE_INF ,pszDEF_GENERATE_INF ,vtypeBOOL,vflPERM ,fnvcvBool }, +{pszVAR_INF_CAB_HEADER ,pszDEF_INF_CAB_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_CAB_LINE_FMT ,pszDEF_INF_CAB_LINE_FMT ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_COMMENT_STRING ,pszDEF_INF_COMMENT_STRING ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_DATE_FMT ,pszDEF_INF_DATE_FMT ,vtypeSTR ,vflPERM|vflCOPY,fnvcvDateFmt }, +{pszVAR_INF_DISK_HEADER ,pszDEF_INF_DISK_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_DISK_LINE_FMT ,pszDEF_INF_DISK_LINE_FMT ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FILE_HEADER ,pszDEF_INF_FILE_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FILE_LINE_FMT ,pszDEF_INF_FILE_LINE_FMT ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FILE_NAME ,pszDEF_INF_FILE_NAME ,vtypeSTR ,vflPERM ,fnvcvFile }, +{pszVAR_INF_FOOTER ,pszDEF_INF_FOOTER ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FOOTER1 ,pszDEF_INF_FOOTER1 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FOOTER2 ,pszDEF_INF_FOOTER2 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FOOTER3 ,pszDEF_INF_FOOTER3 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_FOOTER4 ,pszDEF_INF_FOOTER4 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER ,pszDEF_INF_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER1 ,pszDEF_INF_HEADER1 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER2 ,pszDEF_INF_HEADER2 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER3 ,pszDEF_INF_HEADER3 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER4 ,pszDEF_INF_HEADER4 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER5 ,pszDEF_INF_HEADER5 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_HEADER6 ,pszDEF_INF_HEADER6 ,vtypeSTR ,vflPERM|vflCOPY,NULL }, +{pszVAR_INF_SECTION_ORDER ,pszDEF_INF_SECTION_ORDER ,vtypeSTR ,vflPERM|vflCOPY,fnvcvSectionOrder}, +{pszVAR_MAX_CABINET_SIZE ,pszDEF_MAX_CABINET_SIZE ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_MAX_DISK_FILE_COUNT ,pszDEF_MAX_DISK_FILE_COUNT ,vtypeLONG,vflPERM ,fnvcvMaxDiskFileCount}, +{pszVAR_MAX_DISK_SIZE ,pszDEF_MAX_DISK_SIZE ,vtypeLONG,vflPERM ,fnvcvMaxDiskSize}, +{pszVAR_MAX_ERRORS ,pszDEF_MAX_ERRORS ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_RESERVE_PER_CABINET ,pszDEF_RESERVE_PER_CABINET ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_RESERVE_PER_DATA_BLOCK ,pszDEF_RESERVE_PER_DATA_BLOCK ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_RESERVE_PER_FOLDER ,pszDEF_RESERVE_PER_FOLDER ,vtypeLONG,vflPERM ,fnvcvLong }, +{pszVAR_RPT_FILE_NAME ,pszDEF_RPT_FILE_NAME ,vtypeSTR ,vflPERM ,fnvcvFile }, +{pszVAR_DIR_SRC ,pszDEF_DIR_SRC ,vtypeSTR ,vflPERM ,fnvcvDirSrc }, +{pszVAR_UNIQUE_FILES ,pszDEF_UNIQUE_FILES ,vtypeBOOL,vflPERM ,fnvcvBool }, +}; + + +//** acsatMap -- Map command string to command type + +typedef struct { + char *pszName; // Command string + COMMANDTYPE ct; // Command type +} COMMAND_STRING_AND_TYPE; /* csat */ + +COMMAND_STRING_AND_TYPE acsatMap[] = { + { pszCMD_DEFINE , ctDEFINE }, // Define + { pszCMD_DELETE , ctDELETE }, // Delete + { pszCMD_DUMP , ctDUMP }, // Dump + { pszCMD_INF_BEGIN , ctINF_BEGIN }, // InfBegin + { pszCMD_INF_END , ctINF_END }, // InfEnd + { pszCMD_INF_WRITE , ctINF_WRITE }, // InfWrite + { pszCMD_INF_WRITE_CAB , ctINF_WRITE_CAB }, // InfWriteCabinet + { pszCMD_INF_WRITE_DISK , ctINF_WRITE_DISK }, // InfWriteDisk + { pszCMD_NEW , ctNEW }, // New + { pszCMD_OPTION , ctOPTION }, // Option + { pszCMD_SET , ctSET }, // Set + { NULL , ctBAD }, // end of list +}; + + +/*** mds -- Map special disk size strings to disk attributes + * + * Data for the amds[] array was gathered using CHKDSK to report + * the cluster size and disk size, and a QBASIC program was used + * to fill up the root directory. + */ + +typedef struct { + char *pszSpecial; // Name used in directive file + char *pszFilesInRoot; // Maximum number of files in root dir + char *pszClusterSize; // Cluster size in bytes + char *pszDiskSize; // Disk size in bytes +} MAP_DISK_SIZE; /* mds */ + +MAP_DISK_SIZE amds[] = { + // tag nFiles cbCluster cbDisk + //-------------- ------- --------- ------------ + {pszVALUE_360K , "112", "1024", "362496"}, // 360K floppy disk + {pszVALUE_720K , "112", "1024", "730112"}, // 720K floppy disk + {pszVALUE_120M , "224", "512", "1213952"}, // 1.2M floppy disk + {pszVALUE_125M , "192", "1024", "1250304"}, // 1.25M (NEC Japan) + {pszVALUE_144M , "224", "512", "1457664"}, // 1.44M floppy disk + {pszVALUE_168M , "16", "2048", "1716224"}, // DMF "1.68M" floppy + {pszVALUE_DMF168, "16", "2048", "1716224"}, // DMF "1.68M" floppy + {pszVALUE_CDROM , "65535", "2048", "681984000"}, // 5 1/4" CD-ROM + +//NOTE: 12-Mar-1994 bens This info supplied by rickdew (Rick Dewitt) +// +// Standard CD has 74-minute capacity. +// Sector size is 2K. +// Sectors per minute is 75. +// Number of sectors = 74*60*75 = 333,000 +// Total bytes = 333,000*2048 = 681,984,000 +// Number of files in the root is unlimited, but MS-DOS limits to 64K +}; +#define nmds (sizeof(amds)/sizeof(MAP_DISK_SIZE)) + + +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// +// Exported Functions +// +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +/*** DFPInit - initialize directive file parser + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +HVARLIST DFPInit(PSESSION psess,PERROR perr) +{ + HVARLIST hvlist; + int i; + + // Create variable list + if (!(hvlist = VarCreateList(perr))) { + return NULL; + } + + //** Define standard variables + for (i=0; i<(sizeof(aiv)/sizeof(INITVAR)); i++) { + if (!VarCreate(hvlist, + aiv[i].pszName, + aiv[i].pszValue, + aiv[i].vtype, + aiv[i].vfl, + aiv[i].pfnvcv, + perr)) { + //** Embellish standard VarCreate error message + strcpy(psess->achMsg,perr->ach); // Save error + + ErrSet(perr,pszDFPERR_CREATE_STD_VAR, + "%s%s",aiv[i].pszName,psess->achMsg); + return NULL; + } + } + + //** Success + return hvlist; +} + + +/*** DFPParse - Parse a directive file + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +BOOL DFPParse(PSESSION psess, + HTEXTFILE htfDF, + PFNDIRFILEPARSE pfndfp, + PERROR perr) +{ + COMMAND cmd; + char achLine[cbMAX_DF_LINE]; + int iLine; + + AssertSess(psess); + SetAssertSignature((&cmd),sigCOMMAND); + + iLine = 0; + + //** Parse directives + while (!TFEof(htfDF)) { + //** Get a line + if (!TFReadLine(htfDF,achLine,sizeof(achLine),perr)) { + if (TFEof(htfDF)) { // No Error + return TRUE; + } + else { // Something is amiss + return FALSE; + } + } + iLine++; // Count it + perr->iLine = iLine; // Set line number for error messages + perr->pszLine = achLine; // Set line ptr for error messages + + //** Echo line to output, if verbosity level high enough + if (psess->levelVerbose >= vbMORE) { + printf("%d: %s\n",iLine,achLine); + } + + //** Parse it + getCmdFromLine(&cmd,psess,achLine,perr); + + // Note: Errors in perr are handled by the client! + + //** Give it to client + if (!(*pfndfp)(psess,&cmd,htfDF,achLine,iLine,perr)) { + ClearAssertSignature((&cmd)); + return FALSE; + } + } + + //** Clear signature + ClearAssertSignature((&cmd)); + + //** Success + return TRUE; +} + + +/*** DFPParseVarAssignment - Parse var=value string + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +BOOL DFPParseVarAssignment(PCOMMAND pcmd, PSESSION psess, char *pszArg, PERROR perr) +{ + +//BUGBUG 11-Feb-1994 bens var1=%var2% broken if var2 has trailing spaces +// The problem is that we do variable substitution before any other +// parsing takes place, so the following directives: +// .set var2="one " +// .set var1=%var2% +// Cause us to see the second line as: +// .set var1=one +// and store "one", not "one " as the user might have expected. +// + int cch; + char *pch; + char *pchEnd; + + AssertCmd(pcmd); + AssertSess(psess); + pch = pszArg; + + //** Make sure a variable name is present + if (*pch == '\0') { + ErrSet(perr,pszDFPERR_MISSING_VAR_NAME,"%s",pszCMD_SET); + return FALSE; + } + + //** Find end of variable name + // Var = Value <eos> + // ^ + pchEnd = strpbrk(pch,szDF_SET_CMD_DELIM); // Point after var name + + // Var = Value <eos> + // ^ + if (pchEnd == NULL) { // No assignment operator + ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL); + return FALSE; + } + + //** Make sure variable name is not too long + cch = pchEnd - pch; + if (cch >= cbVAR_NAME_MAX) { + ErrSet(perr,pszDFPERR_VAR_NAME_TOO_LONG,"%d%s",cbVAR_NAME_MAX-1,pch); + return FALSE; + } + + //** Copy var name to command structure, and NUL terminate string + memcpy(pcmd->set.achVarName,pch,cch); + pcmd->set.achVarName[cch] = '\0'; + + //** Make sure assignment operator is present + // Var = Value <eos> + // ^ + pch = pchEnd + strspn(pchEnd,szDF_WHITE_SPACE); + // Var = Value <eos> + // ^ + if (*pch != chDF_EQUAL) { + ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL); + return FALSE; + } + + //** Skip to value. NOTE: Value can be empty, we permit that! + // Var = Value <eos> + // ^ + pch++; // Skip over assignment operator + pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space + // Var = Value + // ^ + + //** Now parse through possibly quoted strings, to end of value + // REMEMBER: We have to trim trailing whitespace + return processLineWithQuotes(pcmd->set.achValue, // destination + sizeof(pcmd->set.achValue), // dest size + pch, // source + szDF_QUOTE_SET, // quoting set + pszDFP_VAR_VALUE, // field name + perr); +} /* DFPParseVarAssignment() */ + + +/*** IsSpecialDiskSize - Check if supplied size is a standard one + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +long IsSpecialDiskSize(PSESSION psess,char *pszDiskSize) +{ + int i; + + i = IMDSfromPSZ(pszDiskSize); // See if special value + if (i != -1) { // Got a special value + return atol(amds[i].pszDiskSize); // Return disk size + } + else { // Not special + return 0; + } +} /* IsSpecialDiskSize() */ + + +/*** lineOut - write line to stdout with padding + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +void lineOut(PSESSION psess, char *pszLine, BOOL fLineFeed) +{ + int cch; + char *pszBlanks; + + //** Do /P (pause) processing + AssertSess(psess); +//BUGBUG 21-Feb-1994 bens Do screen pausing (/P) + + //** Determine how much blank padding, if any, is needed + cch = strlen(pszLine); // Length of line to be written + if (cch >= psess->cchLastLine) { + pszBlanks = psess->achBlanks + cchSCREEN_WIDTH; // Empty + } + else { + pszBlanks = psess->achBlanks + cchSCREEN_WIDTH - + (psess->cchLastLine - cch); + } + + //** Print the line + if (fLineFeed) { + printf("%s%s\n",pszLine,pszBlanks); + cch = 0; // Nothing to overwrite next time + } + else { + printf("%s%s\r",pszLine,pszBlanks); + } + psess->fNoLineFeed = !fLineFeed; + + //** Remember how much to overwrite for next time + psess->cchLastLine = cch; // For overwrite next time +} /* lineOut() */ + + + +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// +// Private Functions +// +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +/*** getCmdFromLine - Construct a command from a directive line + * + * Entry: + * pcmd - Command to fill in after line is parsed + * psess - Session state + * pszLine - Line to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pcmd filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL getCmdFromLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr) +{ + char achLine[cbMAX_DF_LINE]; // Variable-substituted line + int cbDst; + char *pch; // Used to parse pszLine + char *pszSrc; + char *pszDst; + + AssertSess(psess); + pch = pszLine + strspn(pszLine,szDF_WHITE_SPACE); // Skip leading space + + //** Do variable substitution if not in copy to INF mode + if (psess->fCopyToInf) { // Don't edit lines going to INF + pszDst = achLine; + cbDst = sizeof(achLine); + pszSrc = pszLine; + if (!copyBounded(&pszDst,&cbDst,&pszSrc,0)) { + return FALSE; + } + } + else { //** Edit lines that are NOT going straight to an INF area + //** Perform variable substitution on line, including stripping + // comments and any trailing white space! + if (!substituteVariables(achLine,sizeof(achLine), + pszLine,psess->hvlist,perr)) { + return FALSE; // perr already filled in + } + } + + //** Determine the command type, and parse it + pcmd->ct = ctBAD; // Catch errors + pch = achLine + strspn(achLine,szDF_WHITE_SPACE); // Skip leading space + + //** Check for comment lines and blank lines + if ((*pch == chDF_COMMENT) || // Only a comment on the line + (*pch == '\0') ) { // Line is completely blank + pcmd->ct = ctCOMMENT; + return TRUE; + } + + //** Check for directives, etc. + //** JEFFWE - Allow .\file and ..\file even if command prefix is '.' + if ((*pch == chDF_CMD_PREFIX) && + ((chDF_CMD_PREFIX != '.') || + (*(pch+1) != '.') && + (*(pch+1) != '\\'))) { + if (!getCommand(pcmd,psess,achLine,perr)) { + return FALSE; + } + } + else if (psess->fCopyToInf) { + //** Copy a line to an area of the INF file + pcmd->ct = ctINF_WRITE; // Set command type + pcmd->inf.inf = psess->inf; // Use area specified by .InfBegin + pszDst = pcmd->inf.achLine; + cbDst = sizeof(pcmd->inf.achLine); + pszSrc = achLine; + if (!copyBounded(&pszDst,&cbDst,&pszSrc,0)) { + return FALSE; + } + } + else if (psess->fExpectFileCommand) { + //** A file specification + if (!parseFileLine(pcmd,psess,achLine,perr)) { + return FALSE; + } + } + else { + //** An INF file reference + if (!parseReferenceLine(pcmd,psess,achLine,perr)) { + return FALSE; + } + } + + //** Success + return TRUE; +} + + +/*** getCommand - Parse a directive command line + * + * Entry: + * pcmd - Command to fill in after line is parsed + * psess - Session state + * pszLine - Line to parse (already known to have command start char) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pcmd filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL getCommand(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr) +{ + char achCmd[cbMAX_COMMAND_NAME]; // Command name + int cbDst; + int cch; + COMMANDTYPE ct; + char *pch; // Used to parse pszLine + char *pchEnd; // Used to parse pszLine + char *pszSrc; + char *pszDst; + + AssertCmd(pcmd); + AssertSess(psess); + + //** Skip to start of command name + pch = pszLine + strspn(pszLine,szDF_WHITE_SPACE); // Skip white space + Assert(*pch == chDF_CMD_PREFIX); + pch++; // Skip command character + + //** Find end of command name; // Compute length of command name + pchEnd = strpbrk(pch,szDF_WHITE_SPACE); // Point at first char after cmd + if (pchEnd == NULL) { // Command name runs to end of line + cch = strlen(pch); + } + else { + cch = pchEnd - pch; + } + + //** Copy command name to local buffer + if (cch >= cbMAX_COMMAND_NAME) { + ErrSet(perr,pszDFPERR_CMD_NAME_TOO_LONG,"%s",pszLine); + return FALSE; + } + memcpy(achCmd,pch,cch); // Copy it + achCmd[cch] = '\0'; // Terminate it + + //** See if it really is a command + if (ctBAD == (ct = ctFromCommandString(achCmd,perr))) { + return FALSE; // perr has error + } + + //** Set command type + pcmd->ct = ct; + + //** Find start of first argument (if any) + // pch = start of command name + // cch = length of command name + pch += cch; // Point to where argument could start + pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space + + //** Parse remainder of command, as appropriate + // pch = start of first argument (will be '\0' if no arguments present) + switch (ct) { + + case ctCOMMENT: + return TRUE; // Nothing to do + + case ctDEFINE: + //** Syntax is identical to .Set command + return parseSetCommand(pcmd,psess,pch,perr); + + case ctDUMP: // Dump variable settings to stdout + return TRUE; + + case ctDELETE: + //** Make sure a variable name is present + if (*pch == '\0') { + ErrSet(perr,pszDFPERR_MISSING_VAR_NAME,"%s",pszCMD_DELETE); + return FALSE; + } + + //** Make sure only one variable name is present + pchEnd = strpbrk(pch,szDF_WHITE_SPACE); // Skip to end of var name + if (pchEnd != NULL) { + ErrSet(perr,pszDFPERR_BAD_FORMAT,"%s%s",pszCMD_DELETE,pch); + return FALSE; + } + + //** Save variable name + pszDst = pcmd->delete.achVarName; + cbDst = sizeof(pcmd->delete.achVarName); + pszSrc = pch; + if (!copyBounded(&pszDst,&cbDst,&pszSrc,0)) { + return FALSE; + } + return TRUE; + + case ctINF_BEGIN: + if (_stricmp(pch,pszBEGIN_FILE) == 0) { + psess->inf = infFILE; + } + else if (_stricmp(pch,pszBEGIN_CAB) == 0) { + psess->inf = infCABINET; + } + else if (_stricmp(pch,pszBEGIN_DISK) == 0) { + psess->inf = infDISK; + } + else { + ErrSet(perr,pszDFPERR_UNKNOWN_KEYWORD,"%s%s",pszCMD_INF_BEGIN,pch); + return FALSE; + } + psess->fCopyToInf = TRUE; // Turn on copy mode + return TRUE; + + case ctINF_END: + if (!psess->fCopyToInf) { // Not in .InfBegin block + ErrSet(perr,pszDFPERR_END_WITHOUT_BEGIN,"%s%s", + pszCMD_INF_END,pszCMD_INF_BEGIN); + return FALSE; + } + psess->fCopyToInf = FALSE; // Turn off copy mode + psess->inf = infBAD; + return TRUE; + + case ctINF_WRITE: + case ctINF_WRITE_CAB: + case ctINF_WRITE_DISK: + //** Do quote processing and save result in pcmd + if (!processLineWithQuotes(pcmd->inf.achLine, // destination + sizeof(pcmd->inf.achLine), // dest size + pch, // source + szDF_QUOTE_SET, // quoting set + pszDFP_INF_WRITE_STRING, // field name + perr)) { + return FALSE; + } + //** Set are of INF to write + switch (ct) { + case ctINF_WRITE: pcmd->inf.inf = infFILE; break; + case ctINF_WRITE_CAB: pcmd->inf.inf = infCABINET; break; + case ctINF_WRITE_DISK: pcmd->inf.inf = infDISK; break; + + default: + Assert(0); + } + //** Map to single INF write command + pcmd->ct = ctINF_WRITE; + return TRUE; + + case ctNEW: + if (_stricmp(pch,pszNEW_FOLDER) == 0) { + pcmd->new.nt = newFOLDER; + } + else if (_stricmp(pch,pszNEW_CABINET) == 0) { + pcmd->new.nt = newCABINET; + } + else if (_stricmp(pch,pszNEW_DISK) == 0) { + pcmd->new.nt = newDISK; + } + else { + ErrSet(perr,pszDFPERR_UNKNOWN_KEYWORD,"%s%s",pszCMD_NEW,pch); + pcmd->new.nt = newBAD; + return FALSE; + } + return TRUE; + + case ctOPTION: + pcmd->opt.of = 0; // Default is NO<option> + pcmd->opt.ofMask = 0; // No options specified + + //** Construct negated string + strcpy(psess->achMsg,pszOPTION_NEG_PREFIX); + strcat(psess->achMsg,pszOPTION_EXPLICIT); + + if (_stricmp(pch,pszOPTION_EXPLICIT) == 0) { + pcmd->opt.of |= optEXPLICIT; // Explicit is on + pcmd->opt.ofMask |= optEXPLICIT; // Explicit was set + } + else if (_stricmp(pch,psess->achMsg) == 0) { + pcmd->opt.of &= ~optEXPLICIT; // Explicit is off + pcmd->opt.ofMask |= optEXPLICIT; // Explicit was set + } + else { + ErrSet(perr,pszDFPERR_UNKNOWN_KEYWORD,"%s%s",pszCMD_OPTION,pch); + pcmd->new.nt = newBAD; + return FALSE; + } + return TRUE; + + case ctSET: + return parseSetCommand(pcmd,psess,pch,perr); + + case ctBAD: // Bad command + case ctFILE: // Caller handles file copy lines + case ctREFERENCE: // Caller handles reference lines + default: + Assert(0); // Should never get here + return FALSE; + } /* switch (ct) */ + + Assert(0); // Should never get here +} /* getCommand */ + + +/*** parseSetCommand - Parse arguments to .SET command + * + * Entry: + * pcmd - Command to fill in after line is parsed + * psess - Session state + * pszArg - Start of argument string (var=value or var="value") + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pcmd filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * Syntax: + * .SET var=value + * .SET var="value" + */ +BOOL parseSetCommand(PCOMMAND pcmd, PSESSION psess, char *pszArg, PERROR perr) +{ + + //** Parse var=value + if (!DFPParseVarAssignment(pcmd,psess,pszArg,perr)) { + return FALSE; + } + + //** Show parsed var name and value + if (psess->levelVerbose >= vbFULL) { + MsgSet(psess->achMsg,pszDFP_PARSED_SET_CMD, + "%s%s",pcmd->set.achVarName,pcmd->set.achValue); + printf("%s\n",psess->achMsg); + } + + //** Success + return TRUE; +} + + +/*** processLineWithQuotes - Run getQuotedString over the entire line + * + * Entry: + * pszDst - Buffer to receive parsed value + * cbDst - Size of pszDst buffer + * pszSrc - String to parse + * pszQuotes - String of characters that act as quote characters + * pszFieldName - Name of field being parsed (for error message) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pszDst filled in with processed string version of pszSrc + * -- quote characters are processed and removed as appropriate, and + * trailing blanks (outside of quotes) are removed. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * Possible errors: Incorrect delimiter format; + * String too large for pszDst buffer + * + * Syntax of pszSrc is: + * See getQuotedString for details, but essentially this function + * permits the following sorts of effects: + * " foo " => < foo > + * foo => <foo> + * " "foo => < foo> + */ +BOOL processLineWithQuotes(char *pszDst, + int cbDst, + char *pszSrc, + char *pszDelim, + char *pszFieldName, + PERROR perr) +{ + int cch; + int cchValue; + char *pch; + char *pchValue; + + *pszDst = '\0'; + pch = pszSrc; + + while (*pch) { + //** Point at end of value gathered so far + cchValue = strlen(pszDst); + pchValue = pszDst + cchValue; + + //** Copy (possibly quoted) token + pch = getQuotedString(pchValue, + cbDst - cchValue, + pch, + szDF_QUOTE_SET, + pszDFP_INF_WRITE_STRING, // Name of field + perr); + //** Value More <eos> + // ^ + if (pch == NULL) { + return FALSE; // Syntax error or buffer overflow + } + + //** Update current position in destination and size + cchValue = strlen(pszDst); + pchValue = pszDst + cchValue; + + //** Count white space, but copy only if it doesn't end string + cch = strspn(pch,szDF_WHITE_SPACE); + if (*(pch+cch) != '\0') { // Have to copy white space + while ((cch>0) && (cchValue<cbDst)) { + *pchValue++ = *pch++; // Copy character + cchValue++; // Count for buffer overflow test + cch--; + } + if (cchValue >= cbDst) { + ErrSet(perr,pszDFPERR_STRING_TOO_LONG,"%s%d", + pszDFP_INF_WRITE_STRING,cbDst-1); + return FALSE; + } + *pchValue = '\0'; // Keep string well-formed + } + else { + pch += cch; // Make sure we terminate loop + } + } + + //** Success + return TRUE; +} /* processLineWithQuotes() */ + + +/*** getQuotedString - Parse value that may be delimited + * + * Entry: + * pszDst - Buffer to receive parsed value + * cbDst - Size of pszDst buffer + * pszSrc - String to parse + * pszQuotes - String of characters that act as quote characters + * pszFieldName - Name of field being parsed (for error message) + * perr - ERROR structure + * + * Exit-Success: + * Returns pointer to first character after parsed string in pszSrc; + * pszDst filled in with null-terminated string + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + * Possible errors: Incorrect delimiter format; + * String too large for pszDst buffer + * + * Notes: + * (1) If the first character of the string is in the set pszDelim, + * then that character is taken to be the *quote* character, and + * is used to find the end of the string. The leading and trailing + * quote characters are not copied to the pszDst buffer. + * EXAMPLE: <"a phrase"> becomes <a phrase> + * + * (2) If the first character is not in the set pszDelim, then whitespace + * signals the end of the string. + * EXAMPLE: <two words> becomes <two> + * + * (3) If the *quote* character is found immediately following itself + * inside the string, then it is replaced by a single copy of the + * *quote* character and copied to pszDst. + * EXAMPLE: <"He said ""Hi!"" again"> becomes <He said "Hi!" again> + */ +char *getQuotedString(char *pszDst, + int cbDst, + char *pszSrc, + char *pszQuotes, + char *pszFieldName, + PERROR perr) +{ + int cch; + char chQuote; // Quote character + char *pch; // Start of piece + char *pchDst; // Current location in pszDst + char *pchEnd; // End of piece + + Assert(cbDst>0); + + //** Early out for empty string + if (*pszSrc == '\0') { + *pszDst = *pszSrc; // Store empty string + return pszSrc; // Success (pointer does not move) + } + + //** See if first character of string is a quote + for (pch=pszQuotes; (*pch != '\0') && (*pch != pszSrc[0]); pch++) { + //** Scan through pszQuotes looking for a match + } + if (*pch == '\0') { // String is not quoted + //** Get string length + pchEnd = strpbrk(pszSrc,szDF_WHITE_SPACE); + if (pchEnd == NULL) { + cch = strlen(pszSrc); + pchEnd = pszSrc + cch; + } + else { + cch = pchEnd - pszSrc; + } + //** Make sure buffer can hold it + if (cch >= cbDst) { // Won't fit in buffer (need NUL, still) + //** Use field name, and show max string length as one less, + // since that count includes room for a NUL byte. + ErrSet(perr,pszDFPERR_STRING_TOO_LONG,"%s%d",pszFieldName,cbDst-1); + return NULL; // FAILURE + } + memcpy(pszDst,pszSrc,cch); + pszDst[cch] = '\0'; + return pchEnd; // SUCCESS + } + + //** Handle quoted string + chQuote = *pszSrc; // Remember the quote character + pch = pszSrc+1; // Skip over quote character + pchDst = pszDst; // Location to add chars to pszDst + + //** Copy characters until end of string or quote error or buffer overflow + while ((*pch != '\0') && ((pchDst-pszDst) < cbDst)) { + if (*pch == chQuote) { // Got another quote + //** Check for "He said ""Hi"" again" case + if (*(pch+1) == chQuote) { // Need to collapse two quotes + *pchDst++ = *pch++; // Copy a single quote + pch++; // Skip the 2nd quote + } + else { // String is fine, finish and succeed + *pchDst++ = '\0'; // Terminate string + return pch+1; // Return pointer after string + } + } + else { // Normal character + *pchDst++ = *pch++; // Just copy it + } + } + + //** Either we overflowed the buffer, or we missed a closing quote + if ((pchDst-pszDst) >= cbDst) { + ErrSet(perr,pszDFPERR_STRING_TOO_LONG,"%s%d",pszFieldName,cbDst-1); + } + else { + Assert(*pch == '\0'); + ErrSet(perr,pszDFPERR_MISSING_QUOTE,"%c%s",chQuote,pszFieldName); + } + return NULL; // FAILURE +} + + +/*** ctFromCommandString - Map command string to command type + * + * Entry: + * pszCmd - String to check against command list + * perr - ERROR structure + * + * Exit-Success: + * Returns COMMANDTYPE corresponding to pszCmd. + * + * Exit-Failure: + * Returns ctBAD; perr filled in with error. + */ +COMMANDTYPE ctFromCommandString(char *pszCmd, PERROR perr) +{ + int i; + + //** Search for matching command + for (i=0; acsatMap[i].pszName != NULL; i++) { + if (!(_stricmp(acsatMap[i].pszName,pszCmd))) { + //** Found command + return acsatMap[i].ct; // return command type + } + } + + //** Failure + ErrSet(perr,pszDFPERR_UNKNOWN_COMMAND,"%s",pszCmd); + return FALSE; +} /* ctFromCommandString() */ + + +/*** parseReferenceLine - Parse an INF reference line + * + * Entry: + * pcmd - Command to fill in after line is parsed + * psess - Session state + * pszLine - Line to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pcmd filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * Syntax: + * dstFile [/x1=y [y2=y...]] + * + * NOTES: + * (1) Quotes are allowed in file specs -- can you say Long File Names! + */ +BOOL parseReferenceLine(PCOMMAND pcmd,PSESSION psess,char *pszLine,PERROR perr) +{ + int cFiles=0; // Count of file specs seen + char *pch; + BOOL runflag = FALSE; + + AssertCmd(pcmd); + AssertSess(psess); + Assert(psess->ddfmode == ddfmodeRELATIONAL); + + //** Set command type and default values + pcmd->ct = ctREFERENCE; + pcmd->ref.achDst[0] = '\0'; + pcmd->ref.hglist = NULL; // No parameters + + //** Process line + pch = pszLine; + while (*pch != '\0') { + //** Skip whitespace + pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space + if (*pch == '\0') { // End of line + break; // Skip loop so we exit + } + + //** Is this a file name or a parameter? + if (*pch == chDF_MODIFIER) { // A parameter + pch = parseParameterList(&(pcmd->ref.hglist),pch,perr,&runflag); + if (runflag) { + ErrSet(perr,pszDFPERR_RUN_ON_REFERENCE); + goto done; + } + } + else { // A file + cFiles++; + if (cFiles > 1) { // Two many file names + ErrSet(perr,pszDFPERR_EXTRA_JUNK,"%s",pch); + goto done; + } + pch = getQuotedString(pcmd->ref.achDst, + sizeof(pcmd->ref.achDst), + pch, + szDF_QUOTE_SET, + pszDFPERR_DST_FILE, // Name of field + perr); + } + //** Check for error + if (pch == NULL) { + Assert(ErrIsError(perr)); + goto done; + } + } + +done: + //** Need a destination file + if ((cFiles == 0) && !ErrIsError(perr)) { // Don't overwrite existing error + ErrSet(perr,pszDFPERR_MISSING_DST_NAME); + return FALSE; + } + + //** Clean up and exit if an error occured + if (ErrIsError(perr)) { + if (pcmd->ref.hglist) { // Destroy parameter list + GLDestroyList(pcmd->ref.hglist); + pcmd->ref.hglist = NULL; + } + return FALSE; + } + + //** Show parsed dst file name + if (psess->levelVerbose >= vbFULL) { + MsgSet(psess->achMsg,pszDFP_PARSED_REF_CMD,"%s",pcmd->ref.achDst); + printf("%s\n",psess->achMsg); + } + + //** Success + return TRUE; +} /* parseReferenceLine() */ + + +/*** parseFileLine - Parse a file specification line + * + * Entry: + * pcmd - Command to fill in after line is parsed + * psess - Session state + * pszLine - Line to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pcmd filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * Syntax: + * srcFile [dstFile] [/x1=y [y2=y...]] + * + * NOTES: + * (1) Quotes are allowed in file specs -- can you say Long File Names! + */ +BOOL parseFileLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr) +{ + int cFiles=0; // Count of file specs seen + char *pch; + BOOL runflag = FALSE; + + AssertCmd(pcmd); + AssertSess(psess); + + //** Set command type and default values + pcmd->ct = ctFILE; + pcmd->file.achSrc[0] = '\0'; + pcmd->file.achDst[0] = '\0'; + pcmd->file.hglist = NULL; // No parameters + pcmd->file.fRunFlag = FALSE; + + //** Process line + pch = pszLine; + while (*pch != '\0') { + //** Skip whitespace + pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space + if (*pch == '\0') { // End of line + break; // Skip loop so we exit + } + + //** Is this a file name or a parameter? + if (*pch == chDF_MODIFIER) { // A parameter + pch = parseParameterList(&(pcmd->file.hglist),pch,perr,&runflag); + if (runflag == TRUE) { + if (psess->fRunSeen == TRUE) { + ErrSet(perr, pszDFPERR_MULTIPLE_RUN); + goto done; + } + psess->fRunSeen = TRUE; + pcmd->file.fRunFlag = TRUE; + } + + } + else { // A file + cFiles++; + if (cFiles > 2) { // Two many file names + ErrSet(perr,pszDFPERR_EXTRA_JUNK,"%s",pch); + goto done; + } + if (cFiles == 1) { // Get SOURCE file name + pch = getQuotedString(pcmd->file.achSrc, + sizeof(pcmd->file.achSrc), + pch, + szDF_QUOTE_SET, + pszDFPERR_SRC_FILE, // Name of field + perr); + } + else { // Get DESTINATION file name + pch = getQuotedString(pcmd->file.achDst, + sizeof(pcmd->file.achDst), + pch, + szDF_QUOTE_SET, + pszDFPERR_DST_FILE, // Name of field + perr); + } + } + //** Check for error + if (pch == NULL) { + Assert(ErrIsError(perr)); + goto done; + } + } + +done: + //** Need at least a source file + if ((cFiles == 0) && !ErrIsError(perr)) { // Don't overwrite existing error + ErrSet(perr,pszDFPERR_MISSING_SRC_NAME); + return FALSE; + } + + //** Clean up and exit if an error occured + if (ErrIsError(perr)) { + if (pcmd->file.hglist) { // Destroy parameter list + GLDestroyList(pcmd->file.hglist); + pcmd->file.hglist = NULL; + } + return FALSE; + } + + //** Show parsed src and dst file names + if (psess->levelVerbose >= vbFULL) { + MsgSet(psess->achMsg,pszDFP_PARSED_FILE_CMD, + "%s%s",pcmd->file.achSrc,pcmd->file.achDst); + printf("%s\n",psess->achMsg); + } + + //** Success + return TRUE; +} /* parseFileLine() */ + + +/*** parseParameterList - Parse /X=Y parameter list into an HGENLIST + * + * Entry: + * phglist - Pointer to hglist + * pch - Pointer to /X=Y string + * perr - ERROR structure + * + * Exit-Success: + * Returns updated pch, pointing to first character after parsed + * parameter; *phglist created/updated + * + * Exit-Failure; + * Returns NULL; perr filled in. + */ +char *parseParameterList(HGENLIST *phglist, char *pch, PERROR perr, BOOL *runflg) +{ + char achName[cbPARM_NAME_MAX]; // Name buffer + char achValue[cbMAX_DF_LINE]; // Value buffer + int cch; + char *pchEnd; + PFILEPARM pfparm; + + //** Create list if necessary + if (*phglist == NULL) { + //** Create parameter list + *phglist = GLCreateList(NULL, // No default + DestroyFileParm, + pszDFP_FILE_PARM, + perr); + if (!*phglist) { + return NULL; + } + } + + //** Parse name and value + // /X = Y + // ^ + Assert(*pch == chDF_MODIFIER); // Must point to '/' + pch++; // Skip over switch + + //** Make sure parameter name is present + if (*pch == '\0') { + ErrSet(perr,pszDFPERR_MISSING_PARM_NAME); + return NULL; + } + + //** Find end of parameter name + // /X = Y + // ^ + pchEnd = strpbrk(pch,szDF_SET_CMD_DELIM); // Point after var name + + + // /X = Y + // ^ + if (pchEnd == NULL) { // No assignment operator + // So, Check for /RUN directive + if ((strlen(pch) == strlen(pszCMD_RUN)) + && (strncmp( pch, pszCMD_RUN, strlen(pszCMD_RUN)) == 0)) { + *runflg = TRUE; + pch += strlen( pszCMD_RUN ); + return( pch ); + } else { + ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL); + return NULL; + } + } + + //** Make sure parameter name is not too long + cch = pchEnd - pch; + if (cch >= sizeof(achName)) { + ErrSet(perr,pszDFPERR_PARM_NAME_TOO_LONG,"%d%s",sizeof(achName)-1,pch); + return NULL; + } + + //** Copy parameter name, NUL terminate string + memcpy(achName,pch,cch); + achName[cch] = '\0'; + + //** Make sure assignment operator is present + // /X = Y + // ^ + pch = pchEnd + strspn(pchEnd,szDF_WHITE_SPACE); + // Var = Value <eos> + // ^ + if (*pch != chDF_EQUAL) { + ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL); + return NULL; + } + + //** Skip to value. + // /X = Y + // ^ + // NOTE: Value can be empty, we permit that! We have to distinguish + // between: + // /X1= /X2=Y + // and + // /X1=/stuff + // + pch++; // Skip over assignment operator + pchEnd = pch; // Remember where we started scanning + pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space + // /X = Y + // ^ + + if ((*pch == chDF_MODIFIER) && (pch > pchEnd)) { + //** Got special case of /X1= /X2=Y, value is empty + achValue[0] = '\0'; // Value is blank + } + else { + pch = getQuotedString(achValue, + sizeof(achValue), + pch, + szDF_QUOTE_SET, + pszDFP_PARM_VALUE, // Name of field + perr); + if (pch == NULL) { + return NULL; + } + } + + //** Allocate parameter structure + if (!(pfparm = MemAlloc(sizeof(FILEPARM)))) { + ErrSet(perr,pszDFPERR_OUT_OF_MEMORY,"%s",pszDFP_PARM_VALUE); + return NULL; + } + if (!(pfparm->pszValue = MemStrDup(achValue))) { + ErrSet(perr,pszDFPERR_OUT_OF_MEMORY,"%s",pszDFP_PARM_VALUE); + MemFree(pfparm); + return NULL; + } + + //** Add parameter to list + if (!GLAdd(*phglist, // List + achName, // parameter name + pfparm, // parameter value structure + pszDFP_FILE_PARM, // Description for error message + TRUE, // parameter name must be unique + perr)) { + MemFree(pfparm->pszValue); + MemFree(pfparm); + return NULL; + } + //** Set signature after we get it successfully on the list + SetAssertSignature(pfparm,sigFILEPARM); + + //** Return updated parse position + return pch; +} /* parseParameterList() */ + + +/*** substituteVariables - Perform variable substitution; strip comments + * + * Entry: + * pszDst - Buffer to receive substituted version of pszSrc + * cbDst - Size of pszDst + * pszSrc - String to process + * hvlist - Variable list + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pszDst filled in with substituted form + * + * Exit-Failure: + * Returns FALSE; perr filled in with error + * + * Substitution rules: + * (1) Only one level of substitution is performed + * (2) Variable must be defined in hvlist + * (3) "%%" is replaced by "%", if the first % is not the end of + * of a variable substitution. + * (4) Variable substitution is not performed in quoted strings + * (5) End-of-line comments are removed + * (6) Any trailing white space on the line is removed + */ + +BOOL substituteVariables(char *pszDst, + int cbDst, + char *pszSrc, + HVARLIST hvlist, + PERROR perr) +{ + char achVarName[cbVAR_NAME_MAX]; + int cch; + char chQuote; + HVARIABLE hvar; + char *pch; + char *pszAfterVar; // Points to first char after var subst. + char *pszDstSave; // Original pszDst value + char *pszVarNameStart; // Points to first % in var substitution + + pszDstSave = pszDst; + + while (*pszSrc != '\0') { + switch (*pszSrc) { + case chDF_QUOTE1: + case chDF_QUOTE2: + /* + * Copy everything up to closing quote, taking care to handle + * the special case of embedded quotes compatibly with + * getQuotedString! The main issue is to make sure we + * correctly determine the end of the quoted string. + * NOTE: We don't check for quote mismatches here -- we + * just avoid doing variable substituion of comment + * character recognition! + */ + chQuote = *pszSrc; // Remember the quote character + pch = pszSrc + 1; // Skip over first quote + + //** Find end of quoted string + while (*pch != '\0') { + if (*pch == chQuote) { // Got a quote + pch++; // Skip over it + if (*pch != chQuote) { // Marks the end of quoted str + break; // Exit loop and copy string + } + //** If we didn't break out above, it was because + // we had an embedded quote (""). The pch++ above + // skipped over the first quote, and the pch++ + // below skips over the second quote. So, no need + // for any special code! + } + pch++; // Examine next character + } + + //** At this point, we've either found the end of the + // quoted string, or the end of the source buffer. + // we don't care which, as we don't check for errors + // in quoted strings. So we just copy what we found + // and keep going. + + if (!copyBounded(&pszDst,&cbDst,&pszSrc,pch-pszSrc)) { + goto error_copying; + } + break; + + case chDF_COMMENT: // Toss rest of line + goto done; // Finish string and return + + case chDF_SUBSTITUTE: + pszVarNameStart = pszSrc; // Save start for error messgages + pszSrc++; // Skip first % + if (*pszSrc == chDF_SUBSTITUTE) { // Have "%%" + //** Collapse two %% into one % + if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) { + goto error_copying; + } + } + else { + //** Attempt variable substitution + pch = strchr(pszSrc,chDF_SUBSTITUTE); // Finding ending % + if (!pch) { // No terminating % + ErrSet(perr,pszDFPERR_MISSING_SUBST,"%c%s", + chDF_SUBSTITUTE,pszVarNameStart); + return FALSE; + } + pszAfterVar = pch+1; // Point after ending % + + //** Extract variable name + cch = pch - pszSrc; // Length of variable name + if (cch >= cbVAR_NAME_MAX) { + ErrSet(perr,pszDFPERR_VAR_NAME_TOO_LONG,"%d%s", + cbVAR_NAME_MAX-1,pszVarNameStart); + return FALSE; + } + memcpy(achVarName,pszSrc,cch); // Copy it + achVarName[cch] = '\0'; // Terminate it + + //** Look up variable + if (!(hvar = VarFind(hvlist,achVarName,perr))) { + ErrSet(perr,pszDFPERR_VAR_UNDEFINED,"%s", + pszVarNameStart); + return FALSE; + } + + //** Substitute variable + pch = VarGetString(hvar); // Get value + if (!copyBounded(&pszDst,&cbDst,&pch,0)) { + ErrSet(perr,pszDFPERR_VAR_SUBST_OVERFLOW,"%s", + pszVarNameStart); + return FALSE; + } + //** copyBounded appended the NULL byte, but we need to + // remove that so that any subsequent characters on + // the line get tacked on! + pszDst--; // Back up over NULL byte + cbDst++; // Don't count NULL byte + + //** Skip over variable name + pszSrc = pszAfterVar; + } + break; + + default: + //** Just copy the character + if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) { + goto error_copying; + } + break; + } /* switch */ + } /* while */ + +done: + //** Terminate processed string + if (cbDst == 0) { // No room for terminator + goto error_copying; + } + *pszDst++ = '\0'; // Terminate string + + //** Trim off any trailing white space + pch = pszDstSave; // Start at front + while (pch && *pch) { // Process entire string + //** Skip over non-white space + pch = strpbrk(pch,szDF_WHITE_SPACE); + if (pch != NULL) { // Not at the end of the string + //** Skip over white space + cch = strspn(pch,szDF_WHITE_SPACE); + if (*(pch+cch) == '\0') { + //** We're at the end and we have white space + *pch = '\0'; // Trim off the white space + } + else { + pch += cch; // Advance to next non-white space + } + } + } + + //** Success + return TRUE; + +error_copying: + ErrSet(perr,pszDFPERR_COPYING_OVERFLOW,"%s",pszSrc); + return FALSE; +} /* substituteVariables */ + + +/*** BOOLfromPSZ - Get boolean from string value + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +BOOL BOOLfromPSZ(char *psz, PERROR perr) +{ + if (!strcmp(psz,"0") || + !_stricmp(psz,pszVALUE_NO) || + !_stricmp(psz,pszVALUE_OFF) || + !_stricmp(psz,pszVALUE_FALSE)) { + return FALSE; + } + else if (!strcmp(psz,"1") || + !_stricmp(psz,pszVALUE_YES) || + !_stricmp(psz,pszVALUE_ON) || + !_stricmp(psz,pszVALUE_TRUE)) { + return TRUE; + } + else { + ErrSet(perr,pszDFPERR_INVALID_BOOL,"%s",psz); + return -1; + } +} /* BOOLfromPSZ() */ + + +/*** ChecksumWidthFromPSZ - Get Checksum Width from a string + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +int ChecksumWidthFromPSZ(char *psz, PERROR perr) +{ + int level; + int levelLo; + int levelHi; + + level = atoi(psz); + levelLo = atoi(pszCW_LOWEST); + levelHi = atoi(pszCW_HIGHEST); + + //** Check range + if ((levelLo <= level) && (level <= levelHi)) { + return level; + } + + //** Level not in valid range + ErrSet(perr,pszDFPERR_INVALID_CSUM_WIDTH,"%s%s%s", + pszCW_LOWEST,pszCW_HIGHEST,psz); + return -1; +} /* ChecksumWidthFromPSZ() */ + + +/*** CompTypeFromPSZ - Get Compression Type from a string + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +int CompTypeFromPSZ(char *psz, PERROR perr) +{ + if (!_stricmp(psz,pszCT_MSZIP)) { + return tcompTYPE_MSZIP; + } + else if (!_stricmp(psz,pszCT_QUANTUM)) { +#ifdef BIT16 + ErrSet(perr,pszDFPERR_NO_16BIT_QUANTUM); + return -1; +#else // !BIT16 + return tcompTYPE_QUANTUM; +#endif // !BIT16 + } + else { + ErrSet(perr,pszDFPERR_INVALID_COMP_TYPE,"%s",psz); + return -1; + } +} /* CompTypeFromPSZ() */ + + +/*** CompLevelFromPSZ - Get Compression Level from a string + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +int CompLevelFromPSZ(char *psz, PERROR perr) +{ + int level; + int levelLo; + int levelHi; + + level = atoi(psz); + levelLo = atoi(pszCL_LOWEST); + levelHi = atoi(pszCL_HIGHEST); + + //** Check range + if ((levelLo <= level) && (level <= levelHi)) { + return level; + } + + //** Level not in valid range + ErrSet(perr,pszDFPERR_INVALID_COMP_LEVEL,"%s%s%s", + pszCL_LOWEST,pszCL_HIGHEST,psz); + return -1; +} /* CompLevelFromPSZ() */ + + +/*** roundUpToPowerOfTwo - Round up a number to a power of two + * + * Entry: + * x - Number to round up + * + * Exit: + * Returns x rounded up to a power of two: + * x result + * ----- ------ + * 0 0 (???) + * 1 1 (2^0) + * 2 2 (2^1) + * 3 4 (2^2) + * 4 4 (2^2) + * ... .... + * 127 128 (2^7) + * 128 128 (2^7) + * 129 256 (2^8) + * ... ... + */ +long roundUpToPowerOfTwo(long x) +{ + int ibit; + long xSave=x; + long mask; + + //** Check if already a power of 2; We use the trick that clears the + // lowest order 1 bit. If the result is zero, then we know we + // already have a power of 2, since only one 1 bit was set. + if (0 == (x&(x-1))) { + return x; + } + + //** Get the index (1-based) of the most significant 1 bit + for (ibit=0; x; x>>=1, ibit++) + ; + + //** Round up and return result + Assert(ibit >= 2); // First test ensures this + mask = (1 << ibit) - 1; + return (xSave + mask) & ~mask; // Round up to a power of 2 +} /* roundUpToPowerOfTwo() */ + + +/*** CompMemoryFromPSZ - Get Compression Memory from a string + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +int CompMemoryFromPSZ(char *psz, PERROR perr) +{ + long memory; + long memoryLo; // Lowest 2^n exponent allowed + long memoryHi; // Highest 2^n exponent allowed + long cbLo; // Lowest byte count allowed + long cbHi; // Highest byte count allowed + int cbits; + + memory = atoi(psz); + memoryLo = atoi(pszCM_LOWEST); + memoryHi = atoi(pszCM_HIGHEST); + + cbLo = 1L << memoryLo; + cbHi = 1L << memoryHi; + + //** Check 2^n exponent range + if ((memoryLo <= memory) && (memory <= memoryHi)) { + return (int)memory; + } + if (memory < cbLo) { + //** Assume attempted to specify exponent that was too high + ErrSet(perr,pszDFPERR_INVALID_COMP_MEM,"%s%s%s", + pszCM_LOWEST,pszCM_HIGHEST,psz); + return -1; + } + + //** Check byte count range + memory = roundUpToPowerOfTwo(memory); // Make it a power of two + if ((cbLo <= memory) && (memory <= cbHi)) { + //** Take log base 2 + for (cbits=0; memory>>=1; cbits++) + ; + Assert((memoryLo<=cbits) && (cbits<=memoryHi)); + return cbits; + } + + //** Out of range + ErrSet(perr,pszDFPERR_INVALID_COMP_MEM,"%d%d%s", + cbLo,cbHi,psz); + return -1; +} /* CompMemoryFromPSZ() */ + + +/*** fnvcvBool - validate boolean value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvBool) +{ + BOOL f; + + f = BOOLfromPSZ(pszValue,perr); + if (f == -1) { + return FALSE; + } + + if (f == FALSE) { + strcpy(pszNewValue,"0"); + } + else { + Assert(f == TRUE); + strcpy(pszNewValue,"1"); + } + return TRUE; +} + + +/*** fnvcvCabName - Validate CabinetName template + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvCabName) +{ +//BUGBUG 12-Aug-1993 bens Validate CabinetName value + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvChecksumWidth - validate a ChecksumWidth value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvChecksumWidth) +{ + if (-1 == ChecksumWidthFromPSZ(pszValue,perr)) { + return FALSE; + } + + strcpy(pszNewValue,pszValue); + return TRUE; +} /* fnvcvChecksumWidth() */ + + +/*** fnvcvClusterSize - validate a ClusterSize value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + * + * We interpret special strings here that correspond to known disk + * sizes. + */ +FNVCVALIDATE(fnvcvClusterSize) +{ + int i; + + i = IMDSfromPSZ(pszValue); // See if special value + if (i != -1) { // Got a special value + strcpy(pszNewValue,amds[i].pszClusterSize); + return TRUE; + } + else { // Validate long value + return fnvcvLong(hvlist,pszName,pszValue,pszNewValue,perr); + } +} + + +/*** fnvcvCompType - validate a CompressionType value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvCompType) +{ + if (-1 == CompTypeFromPSZ(pszValue,perr)) { + return FALSE; + } + + strcpy(pszNewValue,pszValue); + return TRUE; +} /* fnvcvCompType() */ + + +/*** fnvcvCompLevel - validate a CompressionLevel value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvCompLevel) +{ + if (-1 == CompLevelFromPSZ(pszValue,perr)) { + return FALSE; + } + + strcpy(pszNewValue,pszValue); + return TRUE; +} /* fnvcvCompLevel() */ + + +/*** fnvcvCompMemory - validate a CompressionMemory value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvCompMemory) +{ + if (-1 == CompMemoryFromPSZ(pszValue,perr)) { + return FALSE; + } + + strcpy(pszNewValue,pszValue); + return TRUE; +} /* fnvcvCompMemory() */ + + +/*** fnvcvDateFmt - Validate InfDateFormat value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvDateFmt) +{ + if (!_stricmp(pszValue,pszIDF_MMDDYY) || + !_stricmp(pszValue,pszIDF_YYYYMMDD) ) { + //** Valid date format + strcpy(pszNewValue,pszValue); + return TRUE; + } + //** Unsupported date format + return FALSE; +} /* fnvcvDateFmt() */ + + +/*** fnvcvDirDest - Validate DestinationDir value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvDirDest) +{ +//BUGBUG 12-Aug-1993 bens Validate DestinationDir value + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvDirSrc - Validate SourceDir value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvDirSrc) +{ +//BUGBUG 12-Aug-1993 bens Validate SourceDir value + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvFile - Validate a file name value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvFile) +{ +//BUGBUG 08-Feb-1994 bens Validate file name + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvFileChar - Validate a file name character + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvFileChar) +{ +//BUGBUG 08-Feb-1994 bens Validate file name character + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvLong - validate long value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvLong) +{ + char *psz; + + for (psz=pszValue; *psz && isdigit(*psz); psz++) { + ; //** Make sure entire value is digits + } + if (*psz != '\0') { + ErrSet(perr,pszDFPERR_NOT_A_NUMBER,"%s%s",pszName,pszValue); + return FALSE; + } + + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvMaxDiskFileCount - validate MaxDiskFileCount value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + * + * We interpret special strings here that correspond to known disk + * sizes. + */ +FNVCVALIDATE(fnvcvMaxDiskFileCount) +{ + int i; + + i = IMDSfromPSZ(pszValue); // See if special value + if (i != -1) { // Got a special value + strcpy(pszNewValue,amds[i].pszFilesInRoot); + return TRUE; + } + else { // Validate long value + return fnvcvLong(hvlist,pszName,pszValue,pszNewValue,perr); + } +} + + +/*** fnvcvMaxDiskSize - validate a MaxDiskSize value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvMaxDiskSize) +{ + int i; + + i = IMDSfromPSZ(pszValue); // See if special value + if (i != -1) { // Got a special value + strcpy(pszNewValue,amds[i].pszDiskSize); + return TRUE; + } + else { // Validate long value + return fnvcvLong(hvlist,pszName,pszValue,pszNewValue,perr); + } +} + + +/*** fnvcvSectionOrder - validate InfSectionOrder value + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvSectionOrder) +{ + int bits; + int bitSection; + char ch; + char *psz; + + //** Check length + if (strlen(pszValue) > 3) { + ErrSet(perr,pszDFPERR_BAD_SECTION_ORDER,"%s",pszValue); + return FALSE; + } + + //** Make sure character appears at most once + bits = 0; // Set 1 bit for each character + for (psz=pszValue; *psz; psz++) { + ch = toupper(*psz); + switch (ch) { + case pszISO_DISK: bitSection = 1; break; + case pszISO_CABINET: bitSection = 2; break; + case pszISO_FILE: bitSection = 4; break; + + default: + ErrSet(perr,pszDFPERR_BAD_SECTION_ORDER2,"%c%s",*psz,pszValue); + return FALSE; + } + //** Make sure character is not repeated + if (bits & bitSection) { + ErrSet(perr,pszDFPERR_BAD_SECTION_ORDER3,"%c%s",*psz,pszValue); + return FALSE; + } + bits |= bitSection; // Record this section + } + + //** Value is OK + strcpy(pszNewValue,pszValue); + return TRUE; +} /* fnvcvSectionOrder() */ + + +/*** fnvcvWildFile - validate filename with possibly single "*" char + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvWildFile) +{ +//BUGBUG 12-Aug-1993 bens Validate Wild Filename + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** fnvcvWildPath - validate path with possibly single "*" char + * + * NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions. + */ +FNVCVALIDATE(fnvcvWildPath) +{ +//BUGBUG 12-Aug-1993 bens Validate Wild Path + strcpy(pszNewValue,pszValue); + return TRUE; +} + + +/*** IMDSfromPSZ - Look for special disk designator in amds[] + * + * Entry: + * pszValue - Value to compare against amds[].pszDiskSize values + * + * Exit-Success: + * Returns index in amds[] of entry that matches pszValue; + * + * Exit-Failure: + * Returns -1, pszValue not in amds[] + */ +int IMDSfromPSZ(char *pszValue) +{ + int i; + + for (i=0; + + (i<nmds) && // More special values to check + _stricmp(pszValue,amds[i].pszSpecial) && // String not special + (atol(pszValue) != atol(amds[i].pszDiskSize)); // Value not special + + i++) { + ; // Check for special value + } + + if (i<nmds) { // Got a special value + return i; + } + else { + return -1; + } +} /* IMDSfromPSZ() */ diff --git a/private/windows/diamond/dfparse.h b/private/windows/diamond/dfparse.h new file mode 100644 index 000000000..062271b90 --- /dev/null +++ b/private/windows/diamond/dfparse.h @@ -0,0 +1,376 @@ +/*** dfparse.h - Definitions for Directives File parser + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 12-Aug-1993 bens Removed summary of directives file syntax + * 14-Aug-1993 bens Start working on parser proper + * 21-Aug-1993 bens Return HVARLIST from DFPInit() + * 22-Aug-1993 bens Fleshed out directive parsing + * 10-Feb-1994 bens Added SESSION asserts + * 14-Feb-1994 bens Added FCI context information + * 15-Feb-1994 bens Export DFPParseVarAssignment() + * 16-Feb-1994 bens Add file byte count totals to SESSION + * 21-Feb-1994 bens Add wrapper for stdout writes (lineOut) + * 08-Mar-1994 bens Control INF generation + * 30-Mar-1994 bens Keep track of file counts per folder/cabinet/disk + * 25-Apr-1994 bens Add customizable INF stuff + * 27-May-1994 bens Add CompressionXxxx support + * 28-Mar-1995 jeffwe Add ChecksumWidth variable + */ + +#ifndef INCLUDED_DFPARSE +#define INCLUDED_DFPARSE 1 + +#include <time.h> + +#include "error.h" +#include "textfile.h" +#include "variable.h" +#include "filelist.h" +#include "fileutil.h" // Get cbFILE_NAME_MAX +#include "command.h" + + +/*** HINF - handle to INF context + * + * NOTE: Duplication copied from INF.H to avoid chicken-and-egg definition + * problem. INF.H needs PSESSION, and PSESSION needs HINF! + */ +typedef void *HINF; /* hinf */ + + +#ifdef BIT16 +#include "chuck\fci.h" +#else // !BIT16 +#include "chuck\nt\fci.h" +#endif // !BIT16 + + +//** Random constants +//BUGBUG 21-Feb-1994 bens Hard-coded screen width +#define cchSCREEN_WIDTH 79 // Width of std screen - 1 + + +//** Directive file limits +#define cbMAX_DF_LINE 256 // Maximum length of a directives file line +#define cbMAX_COMMAND_NAME 50 // Maxmimum length of a command name + + +/*** DDFMODE - Mode of INF generation indicated by DDF + * + */ +typedef enum { + ddfmodeBAD, + + ddfmodeUNKNOWN, + ddfmodeUNIFIED, + ddfmodeRELATIONAL, +} DDFMODE; /* ddfmode */ + + +/*** ACTION - What action Diamond command line requested + * + */ +typedef enum { + actBAD, // Invalid action + actHELP, // Show help + actFILE, // Compress a single file specification + actDIRECTIVE, // Process a directives file +} ACTION; /* act */ + + +/*** VERBOSITY - Verbosity of debug output + * + */ +typedef enum { + vbNONE, // Minimal output during disk layout + vbSOME, // Only high-level status feedback + vbMORE, // Medium level status feedback + vbFULL, // Lots of feedback! +} VERBOSITY; + +#ifdef ASSERT +#define sigSESSION MAKESIG('S','E','S','S') // SESSION signature +#define AssertSess(psess) AssertStructure(psess,sigSESSION); +#else // !ASSERT +#define AssertSess(psess) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigSESSION +#endif + ACTION act; // Action to perform + HFILELIST hflistDirectives; // Directive file list, or single file + HVARLIST hvlist; // List of variables + HVARLIST hvlistPass2; // List of variables for pass 2 + BOOL fPass2; // Doing pass 2 processing + int iDisk; // Current disk number + int iCabinet; // Current cabinet number + int iFolder; // Current folder number + long cbDiskLeft; // Count of bytes left on current disk + long cFilesInFolder; // Count of files in current folder + long cFilesInCabinet; // Count of files in current cabinet + long cFilesInDisk; // Count of files in current disk + long cbCabinetEstimate; // Estimated size of last cabinet + long cbClusterCabEst; // Custer size for estimated cabinet + int cErrors; // Count of errors encountered + int cWarnings; // Count of warnings encountered + VERBOSITY levelVerbose; // Verbosity level + HFCI hfci; // FCI context + ERF erf; // FCI error structure + BOOL fNoLineFeed; // TRUE if last printf did not have \n + int cchLastLine; // Length of last line written to stdout + ULONG cbTotalFileBytes; // Total bytes in files (from pass 1) + ULONG cbFileBytes; // Running total of bytes processed + ULONG cbFileBytesComp; // Running total of compressed bytes + long cFiles; // Total files in directives file(s) + long iCurrFile; // Index of file in DDF; 0 means no + // file copy commands have been + // processed, yet. + + HINF hinf; // Inf file info + clock_t clkStart; // Time at start of run + clock_t clkEnd; // Time at end of run + BOOL fGenerateInf; // TRUE => generate INF file + USHORT setID; // Cabinet set ID for FCI + HGENLIST hglistFiles; // List of files in session + HGENERIC hgenFile; // Next file in hglistFiles to be placed + HGENERIC hgenFileLast; // Last file added to hglistFiles + + DDFMODE ddfmode; // DDF INF generation mode + BOOL fExpectFileCommand; // TRUE => non-command DDF lines are + // file copy commands; + // FALSE => non-command DDF lines are + // INF reference commands + + BOOL fCopyToInf; // TRUE => Copy lines to INF file + INFAREA inf; // if fCopyToInf is true, area of INF + // lines are being copied to. + + BOOL fExplicitVarDefine; // TRUE => Vars must be .Define before .Set + BOOL fGetVerInfo; // TRUE => Get file version/lang info + BOOL fGetFileChecksum; // TRUE => Compute file checksum + + BOOL fForceNewDisk; // TRUE => Force new disk next time we + // check; get's reset at that time + BOOL fRunSeen; // TRUE => /RUN flag already seen + + char achCurrFile[cbFILE_NAME_MAX]; // Last file sent to FCIAddFile + char achMsg[cbMAX_DF_LINE*2]; // Message formatting buffer + char achBlanks[cchSCREEN_WIDTH+1]; // Buffer of spaces + char achCurrDiskLabel[cbFILE_NAME_MAX]; // Current readable disk label + char achCurrOutputDir[cbFILE_NAME_MAX]; // Current output disk directory +#ifndef REMOVE_CHICAGO_M6_HACK + int fFailOnIncompressible; // TRUE => Fail if a block is incompressible +#endif +} SESSION; /* sess */ +typedef SESSION *PSESSION; /* psess */ + + +/*** PFNDIRFILEPARSE - Function type for DFPParse call back + *** FNDIRFILEPARSE - macro to help define DFPParse call back function + * + * Entry: + * psess - Session + * pcmd - Command to process + * htfDF - Handle to directives file + * pszLine - Line from directives file + * iLine - Line number in directives file + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; continue with parse. + * + * Exit-Failure: + * Returns FALSE; abort parse + * ERROR structure filled in with details of error. + */ +typedef (*PFNDIRFILEPARSE)(PSESSION psess, + PCOMMAND pcmd, + HTEXTFILE htfDF, + char *pszLine, + int iLine, + PERROR perr); +#define FNDIRFILEPARSE(fn) BOOL fn(PSESSION psess, \ + PCOMMAND pcmd, \ + HTEXTFILE htfDF, \ + char *pszLine, \ + int iLine, \ + PERROR perr) + + +/*** DFPInit - initialize directive file parser + * + * Entry: + * psess - Session + * perr - ERROR structure + * + * Exit-Success: + * Returns HVARLIST of standard variables. + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HVARLIST DFPInit(PSESSION psess, PERROR perr); + + +/*** DFPParse - Parse a directive file + * + * Entry: + * psess - Session + * htfDF - Open directive file to parse + * pfndfp - Function to call back after each line is parsed + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; directives file parsed successfully + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL DFPParse(PSESSION psess, + HTEXTFILE htfDF, + PFNDIRFILEPARSE pfndfp, + PERROR perr); + + +/*** DFPParseVarAssignment - Parse var=value string + * + * Entry: + * pcmd - Command to fill in after line is parsed + * psess - Session state + * pszArg - Start of argument string (var=value or var="value") + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pcmd filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * Syntax: + * var=value + * var="value" + * + * NOTES: + * (1) Any leading spaces (between = operator and first non-whitespace + * character) are removed from value, as are any trailing spaces. + * (2) No variable substition is performed by this function. + * + */ +BOOL DFPParseVarAssignment(PCOMMAND pcmd, + PSESSION psess, + char * pszArg, + PERROR perr); + + +/*** IsSpecialDiskSize - Check if supplied size is a standard one + * + * Entry: + * psess - Session state + * pszDiskSize - String with disk size + * + * Exit-Success: + * Returns non-zero disk size; Special size specified + * + * Exit-Failure: + * Returns 0; not a special size + */ +long IsSpecialDiskSize(PSESSION psess,char *pszDiskSize); + + +/*** BOOLfromPSZ - Get boolean from string value + * + * Entry: + * psz - String to convert (YES/NO/TRUE/FALSE/1/0) + * + * Exit-Success: + * Returns BOOL version of string (TRUE or FALSE) + * + * Exit-Failure: + * Returns -1; perr filled in + */ +BOOL BOOLfromPSZ(char *psz, PERROR perr); + + +/*** ChecksumWidthFromPSZ - Get Checksum Width from a string + * + * Entry: + * psz - String to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns checksum width. + * + * Exit-Failure: + * Returns -1; perr filled in. + */ +int ChecksumWidthFromPSZ(char *psz, PERROR perr); + + +/*** CompTypeFromPSZ - Get Compression Type from a string + * + * Entry: + * psz - String to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns compression type. + * + * Exit-Failure: + * Returns -1; perr filled in. + */ +int CompTypeFromPSZ(char *psz, PERROR perr); + + +/*** CompLevelFromPSZ - Get Compression Level from a string + * + * Entry: + * psz - String to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns compression level. + * + * Exit-Failure: + * Returns -1; perr filled in. + */ +int CompLevelFromPSZ(char *psz, PERROR perr); + + +/*** CompMemoryFromPSZ - Get Compression Memory from a string + * + * Entry: + * psz - String to parse + * perr - ERROR structure + * + * Exit-Success: + * Returns compression memory. + * + * Exit-Failure: + * Returns -1; perr filled in. + */ +int CompMemoryFromPSZ(char *psz, PERROR perr); + + +/*** lineOut - write line to stdout with padding + * + * Entry: + * psess - Current session + * pszLine - Line to write + * fLineFeed - TRUE => write line feed (else, just carriage return) + * + * Exit: + * Line written to stdout + */ +void lineOut(PSESSION psess, char *pszLine, BOOL fLineFeed); + +#endif // !INCLUDED_DFPARSE diff --git a/private/windows/diamond/dfparse.msg b/private/windows/diamond/dfparse.msg new file mode 100644 index 000000000..4f4bc3406 --- /dev/null +++ b/private/windows/diamond/dfparse.msg @@ -0,0 +1,481 @@ +/*** dfparse.msg - Displayable strings for dfparse.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 12-Aug-1993 bens Moved from strings.h + * 22-Aug-1993 bens Moved directive file text from diamond.msg + * 09-Feb-1994 bens Update to latest spec + * 25-Apr-1994 bens Add customizable INF stuff + * 02-May-1994 bens Remove commands we'll never implement + * 26-May-1994 bens Add CompressionXxxx variables + * 03-Jun-1994 bens Add .Option, .Define, *ver*, *vers*, *lang*, *csum* + * 29-Jul-1994 bens Add CabinetNameN, DiskDirectoryN + * 06-Aug-1994 bens Remove FailOnMissingSource and DefaultFileSize + * 28-Mar-1995 jeffwe Add ChecksumWidth variable + */ + +//** Directive File special characters + +#define chDF_WILDCARD '*' // Character to indicate replacement + // Ex: .Set DiskDirectoryTemplate=DISK<*> + +#define chDF_CMD_PREFIX '.' // Command prefix character + // Ex: <.>Set foo=bar + +#define chDF_COMMENT ';' // Comment character + // Ex: <;>This is a comment + +#define chDF_SUBSTITUTE '%' // Variable substitution character + // Ex: .Set Foo=<%>aVariable<%> + +#define chDF_MODIFIER '/' // Indicates start of directive modifier + // Ex: foo.exe </>date=04/10/82 + +#define chDF_EQUAL '=' // Assignment operator (see szDF_SET_CMD_DELIM) + // Ex: .Set Foo<=>Bar + +#define szDF_WHITE_SPACE " \t" // White space (blank or tab) + +#define szDF_SET_CMD_DELIM "= \t" // End var name in .SET (see chDF_EQUAL) + // Also for file copy command parameters! + +#define chDF_QUOTE1 '\"' // Double quote +#define chDF_QUOTE2 '\'' // Apostrophe (aka: single quote) + +#define szDF_QUOTE_SET "'\"" // ' and " are valid quote characters for + // delimiting quoted strings. + // Ex: .Set Foo=<">A string with blanks<"> + + +//** Directive File commands + +#define pszCMD_DEFINE "Define" // Define a new variable +#define pszCMD_DELETE "Delete" // Delete a user-defined var +#define pszCMD_DUMP "Dump" // Dump out variable settings + +//** NOTE: There is no pszCMD_FILE -- lines without a directive are these! +#define pszFILE_INF "Inf" // /Inf parm on file copy line +#define pszFILE_UNIQUE "Unique" // /Unique parm on file copy line + +#define pszCMD_INF_BEGIN "InfBegin" +#define pszBEGIN_CAB "Cabinet" +#define pszBEGIN_DISK "Disk" +#define pszBEGIN_FILE "File" +#define pszCMD_INF_END "InfEnd" +#define pszCMD_INF_WRITE "InfWrite" // Write to file area +#define pszCMD_INF_WRITE_CAB "InfWriteCabinet" // Write to cabinet area +#define pszCMD_INF_WRITE_DISK "InfWriteDisk" // Write to disk area +#define pszCMD_NEW "New" +#define pszNEW_CABINET "Cabinet" // Option on .New directive +#define pszNEW_DISK "Disk" // Option on .New directive +#define pszNEW_FOLDER "Folder" // Option on .New directive +#define pszCMD_OPTION "Option" +#define pszOPTION_NEG_PREFIX "No" // Prefix string to negate +#define pszOPTION_EXPLICIT "Explicit" // Require .Define for .Set +#define pszCMD_SET "Set" // Change variable setting + + +//** RUN flag for the single file to be run +// + +#define pszCMD_RUN "RUN" + + +//** Directive File standard variables + +#define pszVALUE_NOT_DEFINED "" + +#define pszVALUE_ON "On" +#define pszVALUE_YES "Yes" +#define pszVALUE_TRUE "True" + +#define pszVALUE_OFF "Off" +#define pszVALUE_NO "No" +#define pszVALUE_FALSE "False" + +#define pszVALUE_360K "360K" +#define pszVALUE_720K "720K" +#define pszVALUE_120M "1.2M" +#define pszVALUE_125M "1.25M" +#define pszVALUE_144M "1.44M" +#define pszVALUE_168M "1.68M" +#define pszVALUE_DMF168 "DMF168" +#define pszVALUE_CDROM "CDROM" + +#define pszVAR_CABINET "Cabinet" +#define pszDEF_CABINET pszVALUE_ON + +#define pszVAR_CABINET_FILE_COUNT_THRESHOLD "CabinetFileCountThreshold" +#define pszDEF_CABINET_FILE_COUNT_THRESHOLD "0" // No threshold + +#define pszVAR_CAB_NAME "CabinetNameTemplate" +#define pszDEF_CAB_NAME "*.cab" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file to +// override the CabinetNameTemplate value for cabinet names. +// +#define pszPATTERN_VAR_CAB_NAME "CabinetName*" + + +#define pszVAR_CHECKSUM_WIDTH "ChecksumWidth" +#define pszCW_LOWEST "1" // Need at least one hex char +#define pszCW_DEFAULT "8" // Default to all 8 +#define pszCW_HIGHEST "8" // Only 8 hex chars in a long +#define pszDEF_CHECKSUM_WIDTH pszCW_DEFAULT // Default to 8 characters + +#define pszVAR_CLUSTER_SIZE "ClusterSize" +#define pszDEF_CLUSTER_SIZE pszVALUE_144M // Default to 1.44M floppy + +#define pszVAR_COMPRESS "Compress" +#define pszDEF_COMPRESS pszVALUE_ON + +#define pszVAR_COMP_FILE_EXT_CHAR "CompressedFileExtensionChar" +#define pszDEF_COMP_FILE_EXT_CHAR "_" + +#define pszVAR_COMPRESSION_TYPE "CompressionType" +#define pszCT_MSZIP "MSZIP" +#define pszCT_QUANTUM "Quantum" +#define pszDEF_COMPRESSION_TYPE pszCT_MSZIP + +#define pszVAR_COMPRESSION_LEVEL "CompressionLevel" +#define pszCL_LOWEST "1" // Fastest +#define pszCL_DEFAULT "2" // Compromise +#define pszCL_HIGHEST "7" // Best, but very slow +#define pszDEF_COMPRESSION_LEVEL pszCL_DEFAULT + +#define pszVAR_COMPRESSION_MEMORY "CompressionMemory" +#define pszCM_LOWEST "10" //2^10 == 1Kb +#define pszCM_DEFAULT "18" //2^18 == 256Kb +#define pszCM_HIGHEST "21" //2^21 == 2Mb +#define pszDEF_COMPRESSION_MEMORY pszCM_DEFAULT + +#define pszVAR_DIR_DEST "DestinationDir" +#define pszDEF_DIR_DEST "" + +#define pszVAR_DISK_LABEL_NAME "DiskLabelTemplate" +#define pszDEF_DISK_LABEL_NAME "Disk *" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// printed(sticky) disk label names. +// +#define pszPATTERN_VAR_DISK_LABEL "DiskLabel*" + + +#define pszVAR_DISK_DIR_NAME "DiskDirectoryTemplate" +#define pszDEF_DISK_DIR_NAME "disk*" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// overriding DiskDirectoryTemplate to specify a specific directory. +// +#define pszPATTERN_VAR_DISK_DIR "DiskDirectory*" + + +#define pszVAR_DO_NOT_COPY_FILES "DoNotCopyFiles" +#define pszDEF_DO_NOT_COPY_FILES pszVALUE_OFF + +#define pszVAR_FOLDER_FILE_COUNT_THRESHOLD "FolderFileCountThreshold" +#define pszDEF_FOLDER_FILE_COUNT_THRESHOLD "0" // No threshold + +#define pszVAR_FOLDER_SIZE_THRESHOLD "FolderSizeThreshold" +#define pszDEF_FOLDER_SIZE_THRESHOLD "0" // No limit + +#define pszVAR_GENERATE_INF "GenerateInf" +#define pszDEF_GENERATE_INF pszVALUE_ON + +#define pszVAR_INF_FILE_NAME "InfFileName" +#define pszDEF_INF_FILE_NAME "setup.inf" + +#define pszVAR_INF_SECTION_ORDER "InfSectionOrder" +#define pszDEF_INF_SECTION_ORDER "DCF" // Disk, Cabinet, File +#define pszISO_DISK 'D' +#define pszISO_CABINET 'C' +#define pszISO_FILE 'F' + + +/* + * INF file formatting variables + * + */ + +#define pszVAR_INF_COMMENT_STRING "InfCommentString" +#define pszDEF_INF_COMMENT_STRING ";" + + + +#define pszVAR_INF_DISK_HEADER "InfDiskHeader" +#define pszDEF_INF_DISK_HEADER "[disk list]" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// additional INF disk section header lines. +// +#define pszPATTERN_VAR_INF_DISK_HEADER "InfDiskHeader*" + +#define pszVAR_INF_DISK_LINE_FMT "InfDiskLineFormat" +#define pszDEF_INF_DISK_LINE_FMT "*disk#*,*label*" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// specific INF disk line formats +// +#define pszPATTERN_VAR_INF_DISK_LINE_FMT "InfDiskLineFormat*" + + + +#define pszVAR_INF_CAB_HEADER "InfCabinetHeader" +#define pszDEF_INF_CAB_HEADER "[cabinet list]" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// additional INF cabinet section header lines. +// +#define pszPATTERN_VAR_INF_CAB_HEADER "InfCabinetHeader*" + +#define pszVAR_INF_CAB_LINE_FMT "InfCabinetLineFormat" +#define pszDEF_INF_CAB_LINE_FMT "*cab#*,*disk#*,*cabfile*" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// specific INF cabinet line formats +// +#define pszPATTERN_VAR_INF_CAB_LINE_FMT "InfCabinetLineFormat*" + + +#define pszIDF_MMDDYY "mm/dd/yy" +#define pszIDF_YYYYMMDD "yyyy-mm-dd" +#define pszVAR_INF_DATE_FMT "InfDateFormat" +#define pszDEF_INF_DATE_FMT pszIDF_MMDDYY + + +#define pszVAR_INF_FILE_HEADER "InfFileHeader" +#define pszDEF_INF_FILE_HEADER "[file list]" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// additional INF file section header lines. +// +#define pszPATTERN_VAR_INF_FILE_HEADER "InfFileHeader*" + +#define pszVAR_INF_FILE_LINE_FMT "InfFileLineFormat" +#define pszDEF_INF_FILE_LINE_FMT "*disk#*,*cab#*,*file*,*size*" + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// specific INF file line formats +// +#define pszPATTERN_VAR_INF_FILE_LINE_FMT "InfFileLineFormat*" + + + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// additional INF header lines. +// +#define pszPATTERN_VAR_INF_HEADER "InfHeader*" + + +//* The follow variables define the INF header, which is written to the +// INF file before any other text. Additional lines may be defined, +// and the header may be removed entirely by undefining InfHeader. +// %1 = InfCommentString +// %2 = Date/time string +// %3 = Diamond version string +// +#define pszVAR_INF_HEADER "InfHeader" +#define pszDEF_INF_HEADER \ + "%1*** BEGIN **********************************************************" + +#define pszVAR_INF_HEADER1 "InfHeader1" +#define pszDEF_INF_HEADER1 \ + "%1** **" + +#define pszVAR_INF_HEADER2 "InfHeader2" +#define pszDEF_INF_HEADER2 \ + "%1** Automatically generated on: %2 **" + +#define pszVAR_INF_HEADER3 "InfHeader3" +#define pszDEF_INF_HEADER3 \ + "%1** **" + +#define pszVAR_INF_HEADER4 "InfHeader4" +#define pszDEF_INF_HEADER4 \ + "%1** Diamond Version: %3 **" + +#define pszVAR_INF_HEADER5 "InfHeader5" +#define pszDEF_INF_HEADER5 \ + "%1** **" + +#define pszVAR_INF_HEADER6 "InfHeader6" +#define pszDEF_INF_HEADER6 \ + "%1*** BEGIN **********************************************************" + + + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// additional INF header lines. +// +#define pszPATTERN_VAR_INF_FOOTER "InfFooter*" + + +//* The follow variables define the INF footer, which is written to the +// INF file after all other text. Additional lines may be defined, +// and the footer may be removed entirely by undefining InfFooter. +// %1 = InfCommentString +// %2 = Date/time string +// %3 = Diamond version string +// +#define pszVAR_INF_FOOTER "InfFooter" +#define pszDEF_INF_FOOTER \ + "%1*** END ************************************************************" + +#define pszVAR_INF_FOOTER1 "InfFooter1" +#define pszDEF_INF_FOOTER1 \ + "%1** **" + +#define pszVAR_INF_FOOTER2 "InfFooter2" +#define pszDEF_INF_FOOTER2 \ + "%1** Automatically generated on: %2 **" + +#define pszVAR_INF_FOOTER3 "InfFooter3" +#define pszDEF_INF_FOOTER3 \ + "%1** **" + +#define pszVAR_INF_FOOTER4 "InfFooter4" +#define pszDEF_INF_FOOTER4 \ + "%1*** END ************************************************************" + +/* + * Continue with other variables + * + */ + +#define pszVAR_MAX_CABINET_SIZE "MaxCabinetSize" +#define pszDEF_MAX_CABINET_SIZE "0" // Default to disk size + +#define pszVAR_MAX_DISK_FILE_COUNT "MaxDiskFileCount" +#define pszDEF_MAX_DISK_FILE_COUNT pszVALUE_144M // Default to 1.44M limit + +#define pszVAR_MAX_DISK_SIZE "MaxDiskSize" +#define pszDEF_MAX_DISK_SIZE pszVALUE_144M + +//* The following is NOT the name of a variable, but the "template" for +// the variable names that can be defined in the directive file for +// per-disk maximum disk sizes. +// +#define pszPATTERN_VAR_MAX_DISK_SIZE "MaxDiskSize*" + +#define pszVAR_MAX_ERRORS "MaxErrors" +#define pszDEF_MAX_ERRORS "20" // Bail if more than 20 errors + +#define pszVAR_RESERVE_PER_CABINET "ReservePerCabinetSize" +#define pszDEF_RESERVE_PER_CABINET "0" + +#define pszVAR_RESERVE_PER_DATA_BLOCK "ReservePerDataBlockSize" +#define pszDEF_RESERVE_PER_DATA_BLOCK "0" + +#define pszVAR_RESERVE_PER_FOLDER "ReservePerFolderSize" +#define pszDEF_RESERVE_PER_FOLDER "0" + +#define pszVAR_RPT_FILE_NAME "RptFileName" +#define pszDEF_RPT_FILE_NAME "setup.rpt" + +#define pszVAR_DIR_SRC "SourceDir" +#define pszDEF_DIR_SRC "" + +#define pszVAR_UNIQUE_FILES "UniqueFiles" +#define pszDEF_UNIQUE_FILES pszVALUE_ON + + +//** Parameter Names (for File Copy/File Reference commands) + +#define pszPREFIX_INF_VARS "Inf" // Prefix for InfXxx vars that + // are defaults for parameters + +#define pszPARM_FILEATTR "attr" +#define pszPARM_CAB_NUMBER "cab#" +#define pszPARM_CAB_FILE "cabfile" +#define pszPARM_CHECKSUM "csum" +#define pszPARM_FILEDATE "date" +#define pszPARM_DISK_NUMBER "disk#" +#define pszPARM_FILENAME "file" +#define pszPARM_FILE_NUMBER "file#" +#define pszPARM_INF "inf" +#define pszPARM_LABEL "label" +#define pszPARM_LANG "lang" +#define pszPARM_FILESIZE "size" +#define pszPARM_FILETIME "time" +#define pszPARM_UNIQUE "unique" +#define pszPARM_VERNUM "ver" +#define pszPARM_VERSTR "vers" + +#define pszPARM_RUN "RUN" + +//** Status Messages + +#define pszDFP_PARSED_SET_CMD "==> Setting variable %1 to '%2'" +#define pszDFP_PARSED_FILE_CMD "==> FileSpec src=%1 dst=%2" +#define pszDFP_PARSED_REF_CMD "==> Reference dst=%1" + +#define pszDFP_FILE_PARM "file parameter" + + +//** Error Messages + +#define pszDFPERR_CMD_NAME_TOO_LONG "Command name too long: %1" +#define pszDFPERR_COPYING_OVERFLOW "Buffer overflow while copying: %1" +#define pszDFPERR_MISSING_SUBST "Missing %1 after variable name: %2" +#define pszDFPERR_INVALID_VALUE "Bad value for %1: %2" +#define pszDFPERR_INVALID_BOOL "Invalid boolean value: %1" +#define pszDFPERR_UNKNOWN_COMMAND "Unknown command: %1" +#define pszDFPERR_VAR_NAME_TOO_LONG "Variable name exceeds maximum length(%1): %2" +#define pszDFPERR_VAR_SUBST_OVERFLOW "Buffer overflow while substituting variable: %1" +#define pszDFPERR_VAR_UNDEFINED "Variable not defined: %1" +#define pszDFPERR_MISSING_VAR_NAME "No variable name in %1 command" +#define pszDFPERR_MISSING_EQUAL "%1 assignment operator missing" +#define pszDFP_VAR_VALUE "variable value" +#define pszDFPERR_EXTRA_JUNK "Unexpected text: %1" +#define pszDFPERR_STRING_TOO_LONG "%1 exceeded maximum length(%2)" +#define pszDFPERR_MISSING_QUOTE "Missing closing quote(%1) in %2" +#define pszDFP_INF_WRITE_STRING "InfWrite string" +#define pszDFPERR_MISSING_SRC_NAME "Missing source file name" +#define pszDFPERR_MISSING_DST_NAME "Missing destination file name" +#define pszDFPERR_SRC_FILE "source file name" +#define pszDFPERR_DST_FILE "destination file name" +#define pszDFPERR_UNKNOWN_KEYWORD "Unknown keyword in %1 directive: %2" +#define pszDFPERR_MISSING_PARM_NAME "Missing parameter name" +#define pszDFPERR_BAD_FORMAT "Unexpected parameters on %1 command: %2" +#define pszDFPERR_PARM_NAME_TOO_LONG "Parameter name exceeds maximum length(%1): %2" +#define pszDFP_PARM_VALUE "parameter value" +#define pszDFPERR_MULTIPLE_RUN "Multiple file lines specify /RUN flag" +#define pszDFPERR_RUN_ON_REFERENCE "/RUN directive on reference line" + +#define pszDFPERR_NOT_A_NUMBER "Value of variable '%1' must be a number: %2" + + +#define pszDFPERR_OUT_OF_MEMORY "Out of memory saving %1" + +#define pszDFPERR_BAD_SECTION_ORDER "Section order too long: %1" +#define pszDFPERR_BAD_SECTION_ORDER2 "Bad section order character '%1' in '%2'" +#define pszDFPERR_BAD_SECTION_ORDER3 "Duplicate section order character '%1' in '%2'" + +#define pszDFPERR_END_WITHOUT_BEGIN "%1 command without %2 command" + +#define pszDFPERR_INVALID_CSUM_WIDTH "Checksum Width not in range (%1..%2): %3" +#define pszDFPERR_INVALID_COMP_TYPE "Invalid Compression Type: %1" +#define pszDFPERR_INVALID_COMP_LEVEL "Compression Level not in range (%1..%2): %3" +#define pszDFPERR_INVALID_COMP_MEM "Compression Memory not in range (%1..%2): %3" +#ifdef BIT16 +#define pszDFPERR_NO_16BIT_QUANTUM "16-bit DIAMOND.EXE cannot do Quantum -- use 32-bit version" +#endif + +#define pszDFPERR_CREATE_STD_VAR "Failure creating standard variable %1: %2" diff --git a/private/windows/diamond/diamond.c b/private/windows/diamond/diamond.c new file mode 100644 index 000000000..1ccaac044 --- /dev/null +++ b/private/windows/diamond/diamond.c @@ -0,0 +1,4159 @@ +/*** diamond.c - Main program for DIAMOND.EXE + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Improved assertion checking technology + * 14-Aug-1993 bens Removed message test code + * 20-Aug-1993 bens Add banner and command-line help + * 21-Aug-1993 bens Add pass 1 and pass 2 variable lists + * 10-Feb-1994 bens Start of real pass 1/2 work + * 11-Feb-1994 bens .SET and file copy commands -- try calling FCI! + * 14-Feb-1994 bens Call FCI for the first time - it works! + * 15-Feb-1994 bens Support /D and single-file compression + * 16-Feb-1994 bens Update for improved FCI interfaces; disk labels; + * ensure output directories exist + * 20-Feb-1994 bens Move general file routines to fileutil.* so + * extract.c can use them. + * 23-Feb-1994 bens Generate INF file + * 28-Feb-1994 bens Supply new FCI tempfile callback + * 01-Mar-1994 bens Add timing and generate summary report file + * 15-Mar-1994 bens Add RESERVE support + * 21-Mar-1994 bens Updated to renamed FCI.H definitions + * 22-Mar-1994 bens Add english error messages for FCI errors + * 28-Mar-1994 bens Add cabinet setID support + * 29-Mar-1994 bens Fix bug in compressing files w/o extensions + * 30-Mar-1994 bens Layout files outside of cabinets + * 18-Apr-1994 bens Add /L switch + * 20-Apr-1994 bens Fix cabinet/disk size accounting + * 21-Apr-1994 bens Print out c run-time errno in FCI failures + * 22-Apr-1994 bens Add checking for unique file names in cabinet set + * 03-May-1994 bens Add customizable INF stuff + * 27-May-1994 bens Add Quantum support + * 03-Jun-1994 bens Add .Option Explicit, .Define support + * 13-Jul-1994 bens Add DoNotCopyFiles + * 27-Jul-1994 bens Support quotes in .InfWrite[Xxx]; /SIZE qualifier + * for reserving space. + * 14-Dec-1994 bens Implement *csum* support + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <malloc.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <errno.h> +#include <direct.h> + +#ifdef BIT16 +#include <dos.h> +#else // !BIT16 +//** Get minimal Win32 definitions +// In particular, variable.h defines VARTYPE and VARFLAGS, which +// the OLE folks have also defined for OLE automation. +//#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ERROR // Override stupid "#define ERROR 0" in wingdi.h +#endif // !BIT16 + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "dfparse.h" +#include "inf.h" +#include "filelist.h" +#include "fileutil.h" +#include "misc.h" +#include "glist.h" + +#include "diamond.msg" +#include "dfparse.msg" // Get standard variable names + +#include "crc32.h" + + +#ifdef BIT16 +#include "chuck\fci.h" +#else // !BIT16 +#include "chuck\nt\fci.h" +#endif // !BIT16 + +#ifndef BIT16 +#include "filever.h" +#endif // !BIT16 + + +//** Macros + +#define cbDF_BUFFER 4096 // Buffer size for reading directives files + +#define cbFILE_COPY_BUFFER 32768 // Buffer size for copying files + + +//** Types + +typedef struct { + PSESSION psess; + PERROR perr; +} SESSIONANDERROR; /* sae */ +typedef SESSIONANDERROR *PSESSIONANDERROR; /* psae */ + + +//** Function Prototypes + +FNASSERTFAILURE(fnafReport); +FNDIRFILEPARSE(fndfpPassONE); +FNDIRFILEPARSE(fndfpPassTWO); + +BOOL addCmdLineVar(PSESSION psess, char *pszArg, PERROR perr); +HFILESPEC addDirectiveFile(PSESSION psess, char *pszArg, PERROR perr); +HGENERIC addFileToSession(PSESSION psess, + char *pszSrc, + char *pszDst, + long cbFile, + PCOMMAND pcmd, + PERROR perr); +BOOL buildInfAndRpt(PSESSION psess, PERROR perr); +BOOL ccabFromSession(PCCAB pccab, + PSESSION psess, + ULONG cbPrevCab, + PERROR perr); +BOOL checkDiskClusterSize(PSESSION psess, PERROR perr); +BOOL checkReferences(PSESSION psess, PERROR perr); +BOOL checkVariableDefinitions(PSESSION psess, PERROR perr); +void computeSetID(PSESSION psess, char *psz); +BOOL doDefine(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL doDelete(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL doDump(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL doFile(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL doNew(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL doOption(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL doReference(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr); +BOOL ensureCabinet(PSESSION psess, PERROR perr); +BOOL ensureCabinetsFlushed(PSESSION psess, PERROR perr); +BOOL executeCommand(PSESSION psess,PCOMMAND pcmd,BOOL fPass2,PERROR perr); +BOOL getCompressedFileName(PSESSION psess, + char * pszResult, + int cbResult, + char * pszSrc, + PERROR perr); +BOOL getFileChecksum(char *pszFile, ULONG *pchecksum, PERROR perr); +long getMaxDiskSize(PSESSION psess, PERROR perr); +BOOL getVarWithOverride(PSESSION psess, + char *pchDst, + int cbDst, + char *pszPattern, + char *pszVar, + int i, + char *pszKind, + PERROR perr); +BOOL inCabinet(PSESSION psess, PERROR perr); +char *mapCRTerrno(int errno); +BOOL modeInfAddLine(PSESSION psess, + INFAREA inf, + char *pszLine, + PERROR perr); +BOOL modeInfAddFile(PSESSION psess, + char *pszFile, + int iDisk, + int iCabinet, + PERROR perr); +BOOL newDiskIfNecessary(PSESSION psess, + long cbConsume, + BOOL fSubOnNewDisk, + PERROR perr); +BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr); +void printError(PSESSION psess, PERROR perr); +BOOL processDirectives(PSESSION psess, PERROR perr); +BOOL processFile(PSESSION psess, PERROR perr); +void resetSession(PSESSION psess); +BOOL setCabinetReserve(PCCAB pccab, PSESSION psess, PERROR perr); +BOOL setDiskParameters(PSESSION psess, + char *pszDiskSize, + long cbDisk, + PERROR perr); +BOOL setVariable(PSESSION psess, + char *pszName, + char *pszValue, + PERROR perr); +int tcompFromSession(PSESSION psess, PERROR perr); +void updateHgenLast(PSESSION psess, char *pszDst); + +FNOVERRIDEFILEPROPERTIES(fnofpDiamond); + + +//** FCI callbacks +FNALLOC(fciAlloc); +FNFREE(fciFree); +FNFCIGETNEXTCABINET(fciGetNextCabinet); +FNFCIGETNEXTCABINET(fciGetNextCabinet_NOT); +FNFCIFILEPLACED(fciFilePlaced); +FNFCIGETOPENINFO(fciOpenInfo); +FNFCISTATUS(fciStatus); +FNFCIGETTEMPFILE(fciTempFile); + +void mapFCIError(PERROR perr, PSESSION psess, char *pszCall, PERF perf); + + +//** Functions + +/*** main - Diamond main program + * + * See DIAMOND.DOC for spec and operation. + * + * NOTE: We're sloppy, and don't free resources allocated by + * functions we call, on the assumption that program exit + * will clean up memory and file handles for us. + */ +int __cdecl main (int cArg, char *apszArg[]) +{ + ERROR err; + HVARLIST hvlist; // Variable list for Pass 1 + PSESSION psess; + int rc; // Return code + + AssertRegisterFunc(fnafReport); // Register assertion reporter + MemSetCheckHeap(FALSE); // Turn off slow heap checking + + ErrClear(&err); // No error + err.pszFile = NULL; // No file being processed, yet + + //** Initialize session + psess = MemAlloc(sizeof(SESSION)); + if (!psess) { + ErrSet(&err,pszDIAERR_NO_SESSION); + printError(psess,&err); + exit(1); + } + SetAssertSignature((psess),sigSESSION); +#ifndef REMOVE_CHICAGO_M6_HACK + psess->fFailOnIncompressible = FALSE; // Don't fail on incompressible data +#endif + psess->fExplicitVarDefine = FALSE; // Don't require .Define + psess->fGetVerInfo = FALSE; // Don't get version info + psess->fGetFileChecksum = FALSE; // Don't compute file checksums + psess->fPass2 = FALSE; // Pass 1 + psess->hflistDirectives = NULL; + psess->hvlist = NULL; + psess->hvlistPass2 = NULL; + psess->levelVerbose = vbNONE; // Default to no status + psess->hfci = NULL; + psess->cbTotalFileBytes = 0; // Total bytes in all files + psess->cFiles = 0; + psess->fNoLineFeed = 0; // TRUE if last printf did not have \n + psess->cchLastLine = 0; + psess->hinf = NULL; + psess->hglistFiles = NULL; + psess->setID = 0; // No set ID, yet + psess->achCurrOutputDir[0] = '\0'; // Default is current directory + psess->fForceNewDisk = FALSE; + + memset(psess->achBlanks,' ',cchSCREEN_WIDTH); + psess->achBlanks[cchSCREEN_WIDTH] = '\0'; + resetSession(psess); // Reset pass variables + + //** Print Diamond banner + MsgSet(psess->achMsg,pszBANNER,"%s",pszDIAMOND_VERSION); + printf(psess->achMsg); + + //** Initialize Directive File processor (esp. predefined variables) + if (!(hvlist = DFPInit(psess,&err))) { + printError(psess,&err); + return 1; + } + + //** Parse command line + // NOTE: Must do this after DFPInit, to define standard variables. + psess->hvlist = hvlist; // Command line may modify variables + if (!parseCommandLine(psess,cArg,apszArg,&err)) { + printError(psess,&err); + return 1; + } + + //** Quick out if command line help is requested + if (psess->act == actHELP) { // Do help if any args, for now + printf("\n"); // Separate banner from help + printf(pszCMD_LINE_HELP); +#ifdef ASSERT + printf(pszCMD_LINE_HELP_DBG); +#endif + return 0; + } + + //** Get time at start + psess->clkStart = clock(); + + //** Process command + switch (psess->act) { + case actFILE: + //** Check for any non-standard variable names + if (!checkVariableDefinitions(psess,&err)) { + return 1; // Errors already printed + } + //** Compress the file + if (!processFile(psess,&err)) { + printError(psess,&err); + return 1; + } + break; + + case actDIRECTIVE: + if (!processDirectives(psess,&err)) { + printError(psess,&err); + return 1; + } + break; + + default: + Assert(0); // Should never get here! + } + + //** Determine return code + if (psess->cErrors > 0) { + rc = 1; + } + else { + rc = 0; + } + + //** Free resources + AssertSess(psess); + ClearAssertSignature((psess)); + MemFree(psess); + + //** Indicate result + return rc; +} /* main */ + + +/*** processFile - Process single file compression action + * + * Entry: + * psess - Description of operation to perform + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; file compressed. + * + * Exit-Failure: + * Returns FALSE; perr filled in with details. + */ +BOOL processFile(PSESSION psess, PERROR perr) +{ + char achDst[cbFILE_NAME_MAX];// Destination file name + char achDef[cbFILE_NAME_MAX];// Default destination name + long cbFile; // Size of source file + CCAB ccab; // Cabinet parameters for FCI + BOOL f; + HFILESPEC hfspec; + char *pszSrc; // Source filespec + char *pszDst; // Destination (cabinet) file spec + char *pszFilename; // Name to store in cabinet + SESSIONANDERROR sae; // Context for FCI calls + TCOMP tcomp; + + //** Store context to pass through FCI calls + sae.psess = psess; + sae.perr = perr; + + //** Get src/dst file names + hfspec = FLFirstFile(psess->hflistDirectives); + Assert(hfspec != NULL); // Must have at least one file + pszSrc = FLGetSource(hfspec); + pszDst = FLGetDestination(hfspec); + if ((pszDst == NULL) || (*pszDst == '\0')) { // No destination + //** Generate destination file name + if (!getCompressedFileName(psess,achDef,sizeof(achDef),pszSrc,perr)) { + return FALSE; // perr already filled in + } + pszDst = achDef; // Use constructed name + } + + //** Construct complete filespec for destination file + if (!catDirAndFile(achDst, // gets location+destination + sizeof(achDst), + psess->achCurrOutputDir, // /L argument + pszDst, // destination + "", // no fall back + perr)) { + return FALSE; // perr set already + } + pszDst = achDst; + + //** Make sure source file exists + cbFile = getFileSize(pszSrc,perr); + if (cbFile == -1) { + return FALSE; // perr already filled in + } + psess->cbTotalFileBytes = cbFile; // Save for status callbacks + + //** Get name to store inside of cabinet + pszFilename = getJustFileNameAndExt(pszSrc,perr); + if (pszFilename == NULL) { + return FALSE; // perr already filled in + } + + //** Cabinet controls + ccab.szDisk[0] = '\0'; // No disk label + strcpy(ccab.szCab,pszDst); // Compressed file name (cabinet name) + ccab.szCabPath[0] = '\0'; // No path for cabinet + ccab.cb = 0; // No limit on cabinet size + ccab.cbFolderThresh = ccab.cb; // No folder size limit + ccab.setID = 0; // Set ID does not matter, but make + // it deterministic! +#ifndef REMOVE_CHICAGO_M6_HACK + //** Pass hack flag on to FCI + ccab.fFailOnIncompressible = psess->fFailOnIncompressible; +#endif + + //** Set reserved sizes (from variable settings) + if (!setCabinetReserve(&ccab,psess,perr)) { + return FALSE; + } + + //** Create cabinet + psess->fGenerateInf = FALSE; // Remember we are NOT creating INF + psess->hfci = FCICreate( + &psess->erf, // error code return structure + fciFilePlaced, // callback for file placement notify + fciAlloc, + fciFree, + fciTempFile, + &ccab + ); + if (psess->hfci == NULL) { + mapFCIError(perr,psess,szFCI_CREATE,&psess->erf); + return FALSE; + } + + //** Get compression setting + tcomp = tcompFromSession(psess,perr); + + //** Add file + strcpy(psess->achCurrFile,pszFilename); // Info for fciStatus + psess->cFiles = 1; // Info for fciStatus + psess->iCurrFile = 1; // Info for fciStatus + fciStatus(statusFile,0,0,&sae); // Show new file name, ignore rc + f = FCIAddFile( + psess->hfci, + pszSrc, // filename to add to cabinet + pszFilename, // name to store into cabinet file + FALSE, + fciGetNextCabinet_NOT, // Should never go to a next cabinet! + fciStatus, // Status callback + fciOpenInfo, // Open/get attribs/etc. callback + tcomp, // compression type + &sae // context + ); + if (!f) { + //** Only set error if we didn't already do so in FCIAddFile callback + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_ADD_FILE,&psess->erf); + } + return FALSE; + } + + //** Complete cabinet file + if (!FCIFlushCabinet(psess->hfci,FALSE, + fciGetNextCabinet_NOT,fciStatus,&sae)) { + //** Only set error if we didn't already do so in FCIAddFile callback + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_FLUSH_CABINET,&psess->erf); + } + return FALSE; + } + + //** Destroy FCI context + if (!FCIDestroy(psess->hfci)) { + mapFCIError(perr,psess,szFCI_DESTROY,&psess->erf); + return FALSE; + } + psess->hfci = NULL; // Clear out FCI context + + //** Success + return TRUE; +} /* processFile() */ + + +/*** fciGetNextCabinet_NOT - FCI calls this to get new cabinet info + * + * NOTE: This should never get called, as we are compressing a single + * file into a cabinet in this case. So set an error! + * + * Entry: + * pccab - Points to previous current-cabinet structure + * cbPrevCab - Size of previous cabinet + * pv - Really a psae + * + * Exit: + * returns FALSE, we should never be called here + */ +FNFCIGETNEXTCABINET(fciGetNextCabinet_NOT) +{ + PSESSION psess = ((PSESSIONANDERROR)pv)->psess; + PERROR perr = ((PSESSIONANDERROR)pv)->perr; + HFILESPEC hfspec; + char *pszSrc; // Source filespec + + //** Get source filespec for error message + AssertSess(psess); + hfspec = FLFirstFile(psess->hflistDirectives); + Assert(hfspec != NULL); // Must have at least one file + pszSrc = FLGetSource(hfspec); + + //** Set the error message + ErrSet(perr,pszDIAERR_MULTIPLE_CABINETS,"%s",pszSrc); + + //** Failure + return FALSE; +} /* fnGetNextCab() */ + + +/*** processDirectives - Process directive file(s) + * + * Entry: + * psess - Description of operation to perform + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; directives processed successfully. + * + * Exit-Failure: + * Returns FALSE; perr filled in with details. + */ +BOOL processDirectives(PSESSION psess, PERROR perr) +{ + ERROR errTmp; // Toss away error + HFILESPEC hfspec; + HTEXTFILE htf=NULL; + HVARIABLE hvar; + char *pszFile; + + //** Must have at least one directives file + AssertSess(psess); + Assert(psess->hflistDirectives != NULL); + + //** Tailor status based on verbosity level + if (psess->levelVerbose == vbNONE) { + //NOTE: This line gets over written below + lineOut(psess,pszDIA_PARSING_DIRECTIVES,FALSE); + } + else { + lineOut(psess,pszDIA_PASS_1_HEADER1,TRUE); + lineOut(psess,pszDIA_PASS_1_HEADER2,TRUE); + } + + //** Save a copy of the variable list in this state for Pass 2 + if (!(psess->hvlistPass2 = VarCloneList(psess->hvlist,perr))) { + goto error; // perr already filled in + } + +/* + ** Pass ONE, make sure everything is OK + */ + hfspec = FLFirstFile(psess->hflistDirectives); + Assert(hfspec != NULL); // Must have at least one file + for (; hfspec != NULL; hfspec = FLNextFile(hfspec)) { + pszFile = FLGetSource(hfspec); + perr->pszFile = pszFile; // Set file for error messages + + //** Open file + if (!(htf = TFOpen(pszFile,tfREAD_ONLY,cbDF_BUFFER,perr))) { + goto error; // perr already filled in + } + + //** Parse it + if (!DFPParse(psess,htf,fndfpPassONE,perr)) { + goto error; // perr already filled in + } + + //** Close it + TFClose(htf); + htf = NULL; // Clear so error path avoids close + } + + //** Not processing directive files; avoid bogus file/line number info + // in any error messages that might be generated below. + perr->pszFile = NULL; + + //** If .Option Explicit, make sure no variables have been .Set without + // being .Defined. + // NOTE: No need to check return value, since any error will increment + // psess->cErrors, and cause us to bail out just below. + if (psess->fExplicitVarDefine) { + checkVariableDefinitions(psess,perr); + } + + //** If in relational mode, make sure there are no unreferenced files + // NOTE: No need to check return value, since any error will increment + // psess->cErrors, and cause us to bail out just below. + checkReferences(psess,perr); + + //** Bail out if any errors in pass 1 + if (psess->cErrors > 0) { + ErrSet(perr,pszDIAERR_ERRORS_IN_PASS_1,"%d",psess->cErrors); + perr->pszFile = NULL; // Not file-specific + goto error; + } + + //** Make sure we can create INF and RPT files *before* we spend any + // time doing compression! We have to do this at the end of processing + // the directive file during pass 1, so that we make sure the INT and + // RPT file names have been specified. Note that only the last + // setting will be used. + // + hvar = VarFind(psess->hvlist,pszVAR_INF_FILE_NAME,perr); + Assert(!perr->fError); // Must be defined + pszFile = VarGetString(hvar); + if (!ensureFile(pszFile,pszDIA_INF_FILE,perr)) { + goto error; + } + + hvar = VarFind(psess->hvlist,pszVAR_RPT_FILE_NAME,perr); + Assert(!perr->fError); // Must be defined + pszFile = VarGetString(hvar); + if (!ensureFile(pszFile,pszDIA_RPT_FILE,perr)) { + goto error; + } + + //** Initialize for INF generation + // NOTE: We use the variable state at the *end* of pass 1, as this + // permits the INF area header variables to be defined anywhere! + // + // NOTE: We check the InfXxxLineFormat variables for undefined + // parameters (so that we can error out before we spend a lot + // of time compressing files!). + // + // NOTE: If any of the ver info parameters (*ver*, *vers*, *lang*) + // are used in the InfFileLineFormat variable, then we note this + // and collect ver info during pass 2. Otherwise, we skip it to + // speed up pass 2. + if (!infCreate(psess,perr)) { + goto error; + } + +/* + ** Pass TWO, do the layout! + */ + psess->fPass2 = TRUE; // Remember for asserts, mostly + + //** Tailor status based on verbosity level + if (psess->levelVerbose >= vbNONE) { + MsgSet(psess->achMsg,pszDIA_STATS_BEFORE,"%,ld%,ld", + psess->cbTotalFileBytes,psess->cFiles); + lineOut(psess,psess->achMsg,TRUE); + //NOTE: This line gets over written below + lineOut(psess,pszDIA_EXECUTING_DIRECTIVES,FALSE); + } + else { + lineOut(psess,pszDIA_PASS_2_HEADER1,TRUE); + lineOut(psess,pszDIA_PASS_2_HEADER2,TRUE); + } + + //** Reset to initial state for pass 2 + if (!VarDestroyList(psess->hvlist,perr)) { + goto error; // perr already filled in + } + psess->hvlist = psess->hvlistPass2; // Use variables saved for pass 2 + psess->hvlistPass2 = NULL; // Clear so error path does not free + resetSession(psess); // Reset pass variables + + //** Process directive files for pass 2 + hfspec = FLFirstFile(psess->hflistDirectives); + Assert(hfspec != NULL); // Must have at least one file + for (; hfspec != NULL; hfspec = FLNextFile(hfspec)) { + pszFile = FLGetSource(hfspec); + perr->pszFile = pszFile; // Set file for error messages + + //** Open file + if (!(htf = TFOpen(pszFile,tfREAD_ONLY,cbDF_BUFFER,perr))) { + goto error; // perr already filled in + } + + //** Parse it + if (!DFPParse(psess,htf,fndfpPassTWO,perr)) { + goto error; // perr already filled in + } + + //** Close it + TFClose(htf); + htf = NULL; // Clear so error path avoids close + } + + //** No longer processing directive files; reset ERROR + perr->pszFile = NULL; + + //** Flush out cabinets, if we have not already done so + if (!ensureCabinetsFlushed(psess,perr)) { + goto error; + } + + //** Get ending time, generate INF and RPT files + psess->clkEnd = clock(); + if (!buildInfAndRpt(psess,perr)) { + goto error; + } + + //** Success + return TRUE; + +error: + if (psess->hinf != NULL) { + infDestroy(psess->hinf,&errTmp); // Ignore errors + } + + if (htf != NULL) { + TFClose(htf); + } + + if (psess->hvlistPass2 != NULL) { + VarDestroyList(psess->hvlistPass2,&errTmp); // Ignore any error + } + + //** Failure + return FALSE; +} + + +/*** buildInfAndRpt - Create INF and RPT output files + * + * Entry: + * psess - Description of operation to perform + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; files created + * + * Exit-Failure: + * Returns FALSE; perr filled in with details. + */ +BOOL buildInfAndRpt(PSESSION psess, PERROR perr) +{ + int hours; + HVARIABLE hvar; + double kbPerSec; + int minutes; + char *pszFile; + double percent; + FILE *pfileRpt; // Report file + double seconds; + double secondsTotal; + time_t timeNow; + + Assert(psess->fGenerateInf); + + //** Compute running time and throughput + secondsTotal = (psess->clkEnd - psess->clkStart) / (float)CLOCKS_PER_SEC; + if (secondsTotal == 0.0) { // Don't divide by zero! + secondsTotal = 1.0; + } + kbPerSec = psess->cbFileBytes/secondsTotal/1024L; + hours = (int)(secondsTotal/(60*60)); // Hours + minutes = (int)((secondsTotal - hours*60*60)/60); // Minutes + seconds = secondsTotal - hours*60*60 - minutes*60; // Seconds + + //** Get date/time stamp + time(&timeNow); + + //** Generate INF file + hvar = VarFind(psess->hvlist,pszVAR_INF_FILE_NAME,perr); + Assert(!perr->fError); // Must be defined + pszFile = VarGetString(hvar); + if (!infGenerate(psess,pszFile,&timeNow,pszDIAMOND_VERSION,perr)) { + return FALSE; + } + if (!infDestroy(psess->hinf,perr)) { + return FALSE; + } + psess->hinf = NULL; // So caller knows it is gone + + //** Display summary of results and write report file + hvar = VarFind(psess->hvlist,pszVAR_RPT_FILE_NAME,perr); + Assert(!perr->fError); // Must be defined + pszFile = VarGetString(hvar); + pfileRpt = fopen(pszFile,"wt"); // Create setup.rpt + if (pfileRpt == NULL) { // Could not create + ErrSet(perr,pszDIAERR_CANT_CREATE_RPT,"%s",pszFile); + printError(psess,perr); + ErrClear(perr); // But, continue + } + + //** Only put header in report file + MsgSet(psess->achMsg,pszDIA_RPT_HEADER,"%s",ctime(&timeNow)); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + //** Show stats on stdout and to report file + MsgSet(psess->achMsg,pszDIA_STATS_AFTER1,"%,13ld",psess->cFiles); + lineOut(psess,psess->achMsg,TRUE); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + MsgSet(psess->achMsg,pszDIA_STATS_AFTER2,"%,13ld",psess->cbFileBytes); + lineOut(psess,psess->achMsg,TRUE); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + MsgSet(psess->achMsg,pszDIA_STATS_AFTER3,"%,13ld",psess->cbFileBytesComp); + lineOut(psess,psess->achMsg,TRUE); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + //** Compute percentage complete + if (psess->cbFileBytes > 0) { + percent = psess->cbFileBytesComp/(float)psess->cbFileBytes; + percent *= 100.0; // Make it 0..100 + } + else { + //** No files, I guess! + percent = 0.0; + } + MsgSet(psess->achMsg,pszDIA_STATS_AFTER4,"%6.2f",percent); + lineOut(psess,psess->achMsg,TRUE); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + MsgSet(psess->achMsg,pszDIA_STATS_AFTER5,"%9.2f%2d%2d%5.2f", + secondsTotal,hours,minutes,seconds); + lineOut(psess,psess->achMsg,TRUE); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + MsgSet(psess->achMsg,pszDIA_STATS_AFTER6,"%9.2f",kbPerSec); + lineOut(psess,psess->achMsg,TRUE); + if (pfileRpt) { + fprintf(pfileRpt,"%s\n",psess->achMsg); + } + + //** Success + return TRUE; +} /* buildInfAndRpt() */ + + +/*** fndfpPassONE - First pass of directives file + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +FNDIRFILEPARSE(fndfpPassONE) +{ + long cMaxErrors; + HVARIABLE hvar; + static char achDDFName[cbFILE_NAME_MAX] = ""; + + AssertSess(psess); + + //** Update line count status occassionaly + if ((psess->levelVerbose == vbNONE) && + (!(perr->iLine % 50) || _stricmp(achDDFName,perr->pszFile))) { + //** Minimal verbosity, and we've processed a 50-line chunk, + // or we've switched DDF files. + MsgSet(psess->achMsg,pszDIA_PARSING_PROGRESS,"%s%d", + perr->pszFile,perr->iLine); + lineOut(psess,psess->achMsg,FALSE); + //** Remember this DDF file name if it changed + if (perr->iLine % 50) { + strcpy(achDDFName,perr->pszFile); + } + } + + //** Execute only if we have no parse error so far + if (!ErrIsError(perr)) { + //** Execute command for pass ONE + executeCommand(psess,pcmd,FALSE,perr); + } + + //** Handle error reporting + if (ErrIsError(perr)) { + //** Print out error + printError(psess,perr); + + //** Make sure we don't exceed our limit + ErrClear(perr); + hvar = VarFind(psess->hvlist,pszVAR_MAX_ERRORS,perr); + Assert(!perr->fError); // MaxErrors must be defined + cMaxErrors = VarGetLong(hvar); + if ((cMaxErrors != 0) && // There is a limit *and* + (psess->cErrors >= cMaxErrors)) { // the limit is exceeded + ErrSet(perr,pszDIAERR_MAX_ERRORS,"%d",psess->cErrors); + perr->pszFile = NULL; // Not specific to a directive file + return FALSE; + } + //** Reset error so we can continue + ErrClear(perr); + } + + + //** Success + return TRUE; +} /* fndfpPassONE() */ + + +/*** fndfpPassTWO - Second pass of directives file + * + * NOTE: See dfparse.h for entry/exit conditions. + */ +FNDIRFILEPARSE(fndfpPassTWO) +{ + AssertSess(psess); + + //** Execute only if we have no parse error so far + if (!ErrIsError(perr)) { + //** Execute command for pass TWO + executeCommand(psess,pcmd,TRUE,perr); + } + + if (ErrIsError(perr)) { + //** Print out error, set abort message and fail + printError(psess,perr); + ErrSet(perr,pszDIAERR_ERRORS_IN_PASS_2); + return FALSE; + } + + //** Success + return TRUE; +} /* fndfpPassTWO() */ + + +/*** executeCommand - execute a parse command + * + * Entry: + * psess - Session + * pcmd - Command to process + * fPass2 - TRUE if this is pass 2, FALSE if pass 1 + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +executeCommand(PSESSION psess, + PCOMMAND pcmd, + BOOL fPass2, + PERROR perr) +{ + AssertSess(psess); + AssertCmd(pcmd); + + //** Execute line + switch (pcmd->ct) { + case ctCOMMENT: + return TRUE; + + case ctDELETE: + return doDelete(psess,pcmd,fPass2,perr); + + case ctDUMP: + return doDump(psess,pcmd,fPass2,perr); + + case ctINF_BEGIN: // Nothing to do + case ctINF_END: // Nothing to do + return TRUE; + + case ctINF_WRITE: + return modeInfAddLine(psess, + pcmd->inf.inf, + pcmd->inf.achLine, + perr); + + case ctNEW: + return doNew(psess,pcmd,fPass2,perr); + + case ctOPTION: + return doOption(psess,pcmd,fPass2,perr); + + case ctFILE: + if (!doFile(psess,pcmd,fPass2,perr) || !fPass2) { + //** Failed or pass 1, toss parameter list + if (pcmd->file.hglist) { + GLDestroyList(pcmd->file.hglist); + } + return FALSE; + } + return TRUE; + + case ctREFERENCE: + if (!doReference(psess,pcmd,fPass2,perr) || !fPass2) { + //** Failed or pass 1, toss parameter list + if (pcmd->ref.hglist) { + GLDestroyList(pcmd->ref.hglist); + } + return FALSE; + } + return TRUE; + + case ctDEFINE: + return doDefine(psess,pcmd,fPass2,perr); + + case ctSET: + return setVariable(psess, + pcmd->set.achVarName, + pcmd->set.achValue, + perr); + + case ctBAD: + case ctINF_WRITE_CAB: // dfparse.c maps to ctINF_WRITE + case ctINF_WRITE_DISK: // dfparse.c maps to ctINF_WRITE + default: + Assert(0); // Should never get here + return FALSE; + } + + //** Should never get here + Assert(0); + return FALSE; +} /* executeCommand() */ + + +/*** setVariable - wrapper around VarSet to do special processing + * + * Entry: + * psess - Session + * pszName - Variable name + * pszValue - New value + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, variable is created (if necessary) and value set. + * + * Exit-Failure: + * Returns FALSE, cannot set variable value. + * ERROR structure filled in with details of error. + * + * Notes: + * (1) For *all* variables, we convert special disk size strings into + * their numeric equivalent. This is the easiest way to ensure + * that we catch MaxDiskSize[n] variables! + * (2) We check for variables with the special vflCOPY flag, and if + * this is *pass 1*, we also set these variables in the *pass 2* + * list; this permits InfDisk/CabinetLineFormat variables to + * be defined in the *inf* section of the DDF, but get used in + * the *layout* section! (Whew, pretty squirrely!) + */ +BOOL setVariable(PSESSION psess, + char *pszName, + char *pszValue, + PERROR perr) +{ + char achSize[50]; + long cbDisk; + HVARIABLE hvar; + char *psz; + + //** Do special disk size list procesing + if (cbDisk = IsSpecialDiskSize(psess,pszValue)) { + _ltoa(cbDisk,achSize,10); // Convert number to string + psz = achSize; + } + else { // Not special + psz = pszValue; + } + + //** Set the variable + if (!(hvar = VarSet(psess->hvlist,pszName,psz,perr))) { + return FALSE; + } + + //** Set it in the pass 2 list if: + // We are not already in pass 2 -and- + // We have already created the pass 2 list -and- + // The variable is supposed to be copied + + if (!psess->fPass2 && + psess->hvlistPass2 && + (VarGetFlags(hvar) & vflCOPY)) { + if (!(hvar = VarSet(psess->hvlistPass2,pszName,psz,perr))) { + return FALSE; + } + } + + //** If MaxDiskSize, update other variables if appropriate + if (_stricmp(pszName,pszVAR_MAX_DISK_SIZE) == 0) { + return setDiskParameters(psess,psz,0,perr); + } + + //** If GenerateInf, do special goofy context processing + if (_stricmp(pszName,pszVAR_GENERATE_INF) == 0) { + switch (psess->ddfmode) { + case ddfmodeUNKNOWN: + //** Let change occur; we don't make up our mind until + // the first file copy command. + return TRUE; + + case ddfmodeUNIFIED: // Doing INF in parallel with file copy + ErrSet(perr,pszDIA_BAD_INF_MODE); + return FALSE; + + case ddfmodeRELATIONAL: + hvar = VarFind(psess->hvlist,pszVAR_GENERATE_INF,perr); + Assert(!perr->fError); // Must be defined + //** Don't allow turning off twice! + if (!VarGetBool(hvar)) { + ErrSet(perr,pszDIA_BAD_INF_MODE); + return FALSE; + } + //** Now we read reference commands + psess->fExpectFileCommand = FALSE; + return TRUE; + + default: + Assert(0); + return FALSE; + } + } + + return TRUE; +} /* setVariable() */ + + +/*** doDump - Process a .DUMP command (dump all variables) + * + * Entry: + * psess - Session + * pcmd - Command to process (ct == ctDUMP) + * fPass2 - TRUE if this is pass 2 + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doDump(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + HVARIABLE hvar; + VARFLAGS vfl; + char *pszName; + char *pszValue; + + AssertSess(psess); + Assert(pcmd->ct == ctDUMP); + + //** Print variable dump header (two line feeds to make sure + // we get a blank line if status output preceded us). + printf("\n\n%s\n",pszDIA_VAR_DUMP1); + printf("%s\n",pszDIA_VAR_DUMP2); + + //** Print out all variables + for (hvar = VarFirstVar(psess->hvlist); + hvar; + hvar = VarNextVar(hvar)) { + + //** Get variable info + vfl = VarGetFlags(hvar); + pszName = VarGetName(hvar); + pszValue = VarGetString(hvar); + + //** Print name and flag (indent for readability) + printf(" %s",pszName); + if (vfl != vflNONE) { + printf("("); + if (vfl & vflPERM) { + printf("%s",pszDIA_VAR_PERMANENT); + } + else if (vfl & vflDEFINE) { + printf("%s",pszDIA_VAR_DEFINED); + } + else { + Assert(0); // Unknown flag + } + printf(")"); + } + + //** Print value + printf("=<%s>\n",pszValue); + } + + //** Success + return TRUE; +} /* doDump() */ + + +/*** doDefine - Process a .DEFINE command + * + * Entry: + * psess - Session to update + * pcmd - Command to process (ct == ctDEFINE) + * fPass2 - TRUE if this is pass 2, where we do the real work! + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doDefine(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + HVARIABLE hvar; + VARFLAGS vfl; + + AssertSess(psess); + Assert(pcmd->ct == ctDEFINE); + + //** Create variable if necessary + hvar = VarFind(psess->hvlist,pcmd->set.achVarName,perr); + if (hvar == NULL) { // Have to create it ourselves + hvar = VarCreate(psess->hvlist, // var list + pcmd->set.achVarName, // var name + "", // default value + vtypeSTR, // var type + vflDEFINE, // var is DEFINED + NULL, // No validation function + perr); + if (hvar == NULL) { + return FALSE; // Could not create variable + } + } + else { + //** Variable already exists, check if .DEFINE is OK + vfl = VarGetFlags(hvar); + if (vfl & vflDEFINE) { + ErrSet(perr,pszDIAERR_REDEFINE,"%s%s", + pszCMD_DEFINE,pcmd->set.achVarName); + return FALSE; + } + else if (vfl & vflPERM) { + ErrSet(perr,pszDIAERR_DEFINE_PERM,"%s%s", + pszCMD_DEFINE,pcmd->set.achVarName); + return FALSE; + } + } + + //** Everything is fine, set the variable value + return setVariable(psess, + pcmd->set.achVarName, + pcmd->set.achValue, + perr); +} /* doDefine() */ + + +/*** doDelete - Process a .DELETE command + * + * Entry: + * psess - Session to update + * pcmd - Command to process (ct == ctDELETE) + * fPass2 - TRUE if this is pass 2, where we do the real work! + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doDelete(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + HVARIABLE hvar; + + AssertSess(psess); + Assert(pcmd->ct == ctDELETE); + + //** Make sure variable exists + if (!(hvar = VarFind(psess->hvlist,pcmd->delete.achVarName,perr))) { + return FALSE; // Variable does not exist + } + + //** Delete it + VarDelete(hvar); + return TRUE; +} /* doDelete() */ + + +/*** doNew - Process a .NEW command + * + * Entry: + * psess - Session to update + * pcmd - Command to process (ct == ctNEW) + * fPass2 - TRUE if this is pass 2, where we do the real work! + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doNew(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + SESSIONANDERROR sae; // Context for FCI calls + char *pszKind; + + AssertSess(psess); + Assert(pcmd->ct == ctNEW); +/* + ** Pass 1, check for invalid context + */ + if (!fPass2) { + //** Don't permit .New commands in INF section of RELATIONAL DDF + if (!psess->fExpectFileCommand) { + ErrSet(perr,pszDIAERR_BAD_CMD_IN_INF_SECT,"%s",pszCMD_NEW); + return FALSE; + } + + //** Don't permit .New cabinet/folder when not in cabinet + switch (pcmd->new.nt) { + case newFOLDER: + case newCABINET: + if (inCabinet(psess,perr)) { + return TRUE; + } + pszKind = (pcmd->new.nt == newFOLDER) ? + pszNEW_FOLDER : pszNEW_CABINET; + ErrSet(perr,pszDIAERR_BAD_NEW_CMD,"%s%s",pszCMD_NEW,pszKind); + return FALSE; + + case newDISK: + return TRUE; + } + Assert(0); // Should never get here + return FALSE; + } + +/* + ** Pass 2, finish the current folder, cabinet, or disk + */ + //** Store context to pass through FCI calls + sae.psess = psess; + sae.perr = perr; + switch (pcmd->new.nt) { + case newFOLDER: + if (!psess->hfci) { // No FCI context yet, so NOP + return TRUE; + } + if (!FCIFlushFolder(psess->hfci,fciGetNextCabinet,fciStatus,&sae)) { + //** Only set error if we didn't already do so + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_FLUSH_FOLDER,&psess->erf); + } + return FALSE; + } + psess->cFilesInFolder = 0; // Reset files in folder count + break; + + case newDISK: + //** Our technique is a lazy one -- we set the flag asking for + // a new disk, and (if in a cabinet) we flush the current + // cabinet. Either way, the next file or cabinet will start + // on a new disk. + psess->fForceNewDisk = TRUE; + if (!inCabinet(psess,perr)) { + return TRUE; // Not in a cabinet, we're done + } + + //** OK, we're in a cabinet; We want to flush it and assume + // that more files are coming for the next cabinet, so + // just fall through to the .New Cabinet code! + // + // ATTENTION: FALLING THROUGH! + + case newCABINET: + if (!psess->hfci) { // No FCI context yet, so NOP + return TRUE; + } + //** Flush current cabinet, but tell FCI more are coming! + if (!FCIFlushCabinet(psess->hfci,TRUE, + fciGetNextCabinet,fciStatus,&sae)) { + //** Only set error if we didn't already do so + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_FLUSH_CABINET,&psess->erf); + } + return FALSE; + } + psess->cFilesInCabinet = 0; // Reset files in folder count + break; + + default: + Assert(0); + return FALSE; + } + return TRUE; +} /* doNew() */ + + +/*** doOption - Process a .OPTION command + * + * Entry: + * psess - Session to update + * pcmd - Command to process (ct == ctOPTION) + * fPass2 - TRUE if this is pass 2 + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doOption(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + AssertSess(psess); + Assert(pcmd->ct == ctOPTION); + + //** Check for a change to the Explicit variable definition setting + if (pcmd->opt.ofMask & optEXPLICIT) { + //** Change to .Option Explicit + psess->fExplicitVarDefine = ((pcmd->opt.of & optEXPLICIT) != 0); + } + + //** Make sure no other bits get snuck in + Assert((pcmd->opt.ofMask & ~optEXPLICIT) == 0); + return TRUE; +} /* doOption() */ + + +/*** doReference - Process an INF file reference + * + * Entry: + * psess - Session to update + * pcmd - Command to process (ct == ctREFERENCE) + * fPass2 - TRUE if this is pass 2, where we do the real work! + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doReference(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + HGENERIC hgen; + PFILEINFO pfinfo; + FILEINFO finfoIgnore; // For ValidateParms + + AssertSess(psess); + Assert(!psess->fExpectFileCommand); + Assert(psess->ddfmode == ddfmodeRELATIONAL); + + //** Find referenced file + if (!(hgen = GLFind(psess->hglistFiles,pcmd->ref.achDst,"",perr))) { + ErrSet(perr,pszDIAERR_REF_FILE_NOT_FOUND,"%s",pcmd->ref.achDst); + return FALSE; + } + + //** Get file info structure + pfinfo = GLGetValue(hgen); + AssertFinfo(pfinfo); + + //** Merge copy of parms from file into reference line + if (!GLCopyToList(&(pcmd->ref.hglist), // Destination + pfinfo->hglistParm, // Source + DuplicateFileParm, + pszDIA_FILE_PARM, + perr)) { + return FALSE; + } + +/* + ** PASS 1 Processing + */ + if (!fPass2) { + //** Validate standard parameters in parameter list + // NOTE: ValidateParms is usually used for File Copy commands, + // so /date, /time, etc. parameters will actually make + // modifications to the finfo structure. However, we don't + // want any modifications to the fileinfo structure for a + // File Reference command. So, we copy the info to a temporary + // structure, and ignore any changes ValidateParms makes. + finfoIgnore = *pfinfo; // Scratch copy of fileinfo + if (!ValidateParms(psess,pcmd->ref.hglist,&finfoIgnore,perr)) { + return FALSE; + } + pfinfo->flags |= fifREFERENCED; // Mark referenced bit + return TRUE; // Everything is fine so far + } + +/* + ** PASS 2 Processing + */ + //** Make sure cabinets are flushed so all file info is filled in + if (!ensureCabinetsFlushed(psess,perr)) { + return FALSE; + } + + //** Add line to file area of INF + if (!infAddFile(psess,pcmd->ref.achDst,pfinfo,pcmd->ref.hglist,perr)) { + return FALSE; // perr already filled in + } + + //** Success + return TRUE; +} /* doReference() */ + + +/*** doFile - Process a file copy command + * + * Entry: + * psess - Session to update + * pcmd - Command to process (ct == ctFILE) + * fPass2 - TRUE if this is pass 2, where we do the real work! + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL doFile(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr) +{ + //** Minimize stack usage + static char achSrc[cbFILE_NAME_MAX]; // Full source file name + static char achDst[cbFILE_NAME_MAX]; // Destination file name + static char achFull[cbFILE_NAME_MAX]; // Full destination file name + long cbFile; + long cMaxFiles; + BOOL fCopyFile; // See DoNotCopyFiles variable + BOOL fSuccess; + HGENERIC hgen; + HVARIABLE hvar; + char *pch; + PFILEINFO pfinfo; + SESSIONANDERROR sae; // Context for FCI calls + TCOMP tcomp; // Compression type + + AssertSess(psess); + Assert(psess->fExpectFileCommand); + +/* + ** Determine INF generation mode on first copy file command + */ + if (psess->ddfmode == ddfmodeUNKNOWN) { + hvar = VarFind(psess->hvlist,pszVAR_GENERATE_INF,perr); + Assert(!perr->fError); // Must be defined + if (VarGetBool(hvar)) { // Generate INF in parallel + psess->ddfmode = ddfmodeUNIFIED; + } + else { + psess->ddfmode = ddfmodeRELATIONAL; + //** Make sure UniqueFiles is ON + hvar = VarFind(psess->hvlist,pszVAR_UNIQUE_FILES,perr); + Assert(!perr->fError); // Must be defined + if (!VarGetBool(hvar)) { // Must be unique + ErrSet(perr,pszDIAERR_MUST_BE_UNIQUE); + return FALSE; + } + } + } + + //** Store context to pass through FCI calls + sae.psess = psess; + sae.perr = perr; + + //** Construct final source and destination filespecs + hvar = VarFind(psess->hvlist,pszVAR_DIR_SRC,perr); + Assert(!perr->fError); // DestinationDir must be defined + pch = VarGetString(hvar); // Get destination dir + if (!catDirAndFile(achSrc,sizeof(achSrc), + pch,pcmd->file.achSrc,NULL,perr)) { + return FALSE; // perr set already + } + + hvar = VarFind(psess->hvlist,pszVAR_DIR_DEST,perr); + Assert(!perr->fError); // SourceDir must be defined + pch = VarGetString(hvar); // Get source dir + if (!catDirAndFile(achDst,sizeof(achDst), + pch,pcmd->file.achDst,pcmd->file.achSrc,perr)) { + return FALSE; // perr set already + } + +/* + ** PASS 1 Processing + */ + if (!fPass2) { + if (psess->levelVerbose >= vbFULL) { + MsgSet(psess->achMsg,pszDIA_FILE_COPY,"%s%s",achSrc,achDst); + lineOut(psess,psess->achMsg,TRUE); + } + + //** Make sure MaxDiskSize is a multiple of the ClusterSize + // NOTE: This only catches cases where MaxDiskSize/ClusterSize + // are not compatible. MaxDiskSizeN variables cannot + // be checked until pass 2, when we know what disk we are + // actually on! Usually we won't get an error in that + // case, since we update the ClusterSize automatically. + if (!checkDiskClusterSize(psess,perr)) { + return FALSE; + } + + //** Make sure file exists + if (-1 == (cbFile = getFileSize(achSrc,perr))) { + return FALSE; // perr already filled in + } + computeSetID(psess,achDst); // Accumulate cabinet setID + psess->cbTotalFileBytes += cbFile; // Count up total file sizes + psess->cFiles++; // Count files + + //** Keep track of file names, handle uniqueness checking + if (!(hgen = addFileToSession(psess,achSrc,achDst,cbFile,pcmd,perr))) { + return FALSE; + } + + //** Make sure standard parameters have valid format + pfinfo = GLGetValue(hgen); + AssertFinfo(pfinfo); + if (!ValidateParms(psess,pfinfo->hglistParm,pfinfo,perr)) { + return FALSE; + } + + //** Allow InfDate, InfTime, InfAttr overrides + // NOTE: This must be called *after* ValidateParms(), so that + // it will not override parms specified on the file copy + // command! + if (!CheckForInfVarOverrides(psess,pfinfo,perr)) { + return FALSE; + } + return TRUE; + } + +/* + ** PASS 2 Processing + */ + + //** Give user status + strcpy(psess->achCurrFile,achDst); // Info for fciStatus + psess->iCurrFile++; // Info for fciStatus + fciStatus(statusFile,0,0,&sae); // Show new file name, ignore rc + + //** Update psess->hgenLast for INF generation + updateHgenLast(psess,achDst); + +#ifndef BIT16 + //** Get Version/Language info (only if needed for INF file) + // NOTE: We only do this in Win32-land, because the Win32 API can + // (usually) handle both 16-bit and 32-bit EXE formats, but + // the 16-bit API cannot handle 32-bit EXE formats. + // Also, we wait until PASS 2 so we know whether the version + // information is needed for the INF file, since it slows us + // down a little to gather it. + if (psess->fGetVerInfo) { + hgen = psess->hgenFileLast; // Item for this file + pfinfo = GLGetValue(hgen); // Get file info + AssertFinfo(pfinfo); + if (!getFileVerAndLang(achSrc, + &(pfinfo->verMS), + &(pfinfo->verLS), + &(pfinfo->pszVersion), + &(pfinfo->pszLang), + perr)) { + return FALSE; + } + } +#endif // !BIT16 + + //** Compute file checksum (only if needed for INF file) + if (psess->fGetFileChecksum) { + hgen = psess->hgenFileLast; // Item for this file + pfinfo = GLGetValue(hgen); // Get file info + AssertFinfo(pfinfo); + if (!getFileChecksum(achSrc,&(pfinfo->checksum),perr)) { + return FALSE; + } + } + + //** Get compression type + tcomp = tcompFromSession(psess,perr); + + //** Determine if we are putting file in cabinet, or straight to disk + if (inCabinet(psess,perr)) { + if (!ensureCabinet(psess,perr)) { // Make sure we have a cabinet + return FALSE; + } + + //** Make sure MaxDiskSize is a multiple of the ClusterSize + if (!checkDiskClusterSize(psess,perr)) { + return FALSE; + } + + //** Get limits on files per folder + hvar = VarFind(psess->hvlist,pszVAR_FOLDER_FILE_COUNT_THRESHOLD,perr); + Assert(!perr->fError); // Must be defined + cMaxFiles = VarGetLong(hvar); // Get file count limit + + //** Check for overrun of files in folder limit + if ((cMaxFiles > 0) && // A limit is set + (psess->cFilesInFolder >= cMaxFiles)) { // Limit is exceeded + if (!FCIFlushFolder(psess->hfci,fciGetNextCabinet,fciStatus,&sae)) { + //** Only set error if we didn't already do so + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_FLUSH_FOLDER,&psess->erf); + } + return FALSE; + } + psess->cFilesInFolder = 0; // Reset files in folder count + } + + //** Add file to folder + if (!FCIAddFile( // Add file to cabinet + psess->hfci, + achSrc, // filename to add to cabinet + achDst, // name to store into cabinet file + pcmd->file.fRunFlag, // Flag indicating execute on extract + fciGetNextCabinet, // callback for continuation cabinet + fciStatus, // status callback + fciOpenInfo, // Open/get attribs/etc. callback + tcomp, // Compression type + &sae + )) { + //** Only set error if we didn't already do so + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_ADD_FILE,&psess->erf); + } + return FALSE; + } + //** Update counts *after* FCIAddFile(), since large files may cause + // us to overflow to a new cabinet (or cabinets), and we want our + // fciGetNextCabinet() callback to reset these counts! + psess->cFilesInFolder++; // Added a file to the current folder + psess->cFilesInCabinet++; // Added a file to the current cabinet + return TRUE; + } + +/* + ** OK, we're putting the file on the disk (not in a cabinet) + */ + //** Check for error from inCabinet() call + if (ErrIsError(perr)) { + return FALSE; + } + +//BUGBUG 19-Apr-1994 bens cabinet=on/cabinet=off disk space accounting broken +// If we are have an open cabinet, and then the DDF file tells us to go +// out of cabinet mode (regardless of the compression setting), then we +// really need to close the open cabinet and update the remaining free space. +// +// This means that if cabinet=on is set later, there will be no connection +// in the cabinet files between the new cabinet set and the old one! +// +// NEED TO DOCUMENT THIS BEHAVIOR IN THE SPEC! + + +//BUGBUG 30-Mar-1994 bens Don't support compressing individual files + if (tcomp != tcompTYPE_NONE) { + ErrSet(perr,pszDIAERR_SINGLE_COMPRESS,"%s",pcmd->file.achSrc); + return FALSE; + } + + //** See if we are copying files; this feature of not copying files + // was implemented for the ACME group, so that their clients can + // generate "ADMIN" INF files quickly -- they do one Diamond run + // with one version of InfFileLineFormat with cabinets and compression + // on, and that generates the "disk" INF. Then they do another + // Diamond run with a different InfFileLineFormat, and they set + // DoNotCopyFiles=TRUE, and turn off cabinets and compression, so + // Diamond goes through all the motions to generate an INF, but + // skips the step of actually copying any files! + + hvar = VarFind(psess->hvlist,pszVAR_DO_NOT_COPY_FILES,perr); + Assert(!perr->fError); // Must be defined + fCopyFile = !VarGetBool(hvar); // Invert setting + + //** Get file info for this file + hgen = psess->hgenFileLast; // Item for this file + pfinfo = GLGetValue(hgen); // Get file info + AssertFinfo(pfinfo); + + //** Switch to new disk if necessary, account for file + if (!newDiskIfNecessary(psess,pfinfo->cbFile,TRUE,perr)) { + return FALSE; + } + + //** Make sure MaxDiskSize is a multiple of the ClusterSize + if (!checkDiskClusterSize(psess,perr)) { + return FALSE; + } + + //** Construct complete filespec for destination file + if (!catDirAndFile(achFull, // gets "disk1\foo\setup.exe" + sizeof(achFull), + psess->achCurrOutputDir, // "disk1" + achDst, // "foo\setup.exe" + "", + perr)) { + return FALSE; // perr set already + } + + //** If copying files, make sure destination directory is created + if (fCopyFile && !ensureDirectory(achFull,TRUE,perr)) { + return FALSE; // perr already filled in + } + + //** Copy file (or just merge file date/time/attr values) +//BUGBUG 01-Apr-1994 bens Pass status to CopyOneFile for more granularity +// Also, should think about separating out data for files that are +// not compressed versus those that are, so we can provide accurate +// statistics! + fSuccess = CopyOneFile(achFull, // Destination file name + achSrc, // Source file name + fCopyFile, // Control whether file is copied + (UINT)cbFILE_COPY_BUFFER, // Copy buffer size + fnofpDiamond, // Overrides date/time/attr + psess, // Context for fnofpDiamond + perr); // ERROR structure + + //** Use true file size for status, ignore return code + fciStatus(statusFile,pfinfo->cbFile,pfinfo->cbFile,&sae); + + //** Add file to INF (cabinet=0 ==> not inside a cabinet) + if (!modeInfAddFile(psess,achDst,psess->iDisk,0,perr)) { + return FALSE; // perr already filled in + } + + return fSuccess; +} /* doFile() */ + + +/*** ensureCabinetsFlushed - Make sure FCI is flushed out + * + * Entry: + * psess - Session + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; cabinets flushed (if necessary) + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL ensureCabinetsFlushed(PSESSION psess, PERROR perr) +{ + SESSIONANDERROR sae; // Context for FCI calls + + AssertSess(psess); + + //** NOP if we already flushed FCI + if (!psess->hfci) { + return TRUE; + } + + //** Store context to pass through FCI calls + sae.psess = psess; + sae.perr = perr; + + //** Flush out any final cabinet remnants + if (!FCIFlushCabinet(psess->hfci,FALSE,fciGetNextCabinet,fciStatus,&sae)) { + //** Only set error if we didn't already do so in FCIAddFile callback + if (!ErrIsError(sae.perr)) { + mapFCIError(perr,psess,szFCI_FLUSH_CABINET,&psess->erf); + } + return FALSE; + } + + //** Destroy FCI context + if (!FCIDestroy(psess->hfci)) { + mapFCIError(perr,psess,szFCI_DESTROY,&psess->erf); + return FALSE; + } + psess->hfci = NULL; // Clear out FCI context + + return TRUE; +} /* ensureCabinetsFlushed() */ + + +/*** computeSetID - accumulate cabinet set ID + * + * The cabinet set ID is used by FDI at decompress time to ensure + * that it received the correct continuation cabinet. We construct + * the set ID for a cabinet set by doing a computation on all of the + * destination files (during pass 1). This is likely to give unique + * set IDs, assuming our function is a good one (which it probably is + * is not). + * + * Entry: + * psess - session to update + * psz - String to "add" to set ID + * + * Exit: + * psess->setID updated; + */ +void computeSetID(PSESSION psess, char *psz) +{ + //** Just add up all the characters, ignoring overflow + while (*psz) { + psess->setID += (USHORT)*psz++; + } +} /* computeSetID() */ + + +/*** inCabinet - Returns indication if cabinet mode is on + * + * Entry: + * psess - Session to check + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; cabinet mode is on + * + * Exit-Failure: + * Returns FALSE; cabinet mode is off, or an error (if perr is set) + */ +BOOL inCabinet(PSESSION psess, PERROR perr) +{ + HVARIABLE hvar; + + hvar = VarFind(psess->hvlist,pszVAR_CABINET,perr); + Assert(!perr->fError); // Must be defined + return VarGetBool(hvar); // Get current setting +} /* inCabinet() */ + + +/*** ensureCabinet - Make sure FCI has a cabinet open + * + * This function is called to create the FCI context. A normal DDF will + * only cause a single FCICreate() call to be made. But a DDF that turns + * cabinet mode on and off several times will cause several FCICreate() + * calls to be made. + * + * Entry: + * psess - Session to update + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; cabinet is ready to receive files + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL ensureCabinet(PSESSION psess, PERROR perr) +{ + CCAB ccab; + + AssertSess(psess); + if (psess->hfci == NULL) { // Have to create context + //** Set CCAB for FCICreate + if (!ccabFromSession(&ccab,psess,0,perr)) { // No previous cabinet size + return FALSE; + } + + //** Set reserved sizes (from variable settings) + if (!setCabinetReserve(&ccab,psess,perr)) { + return FALSE; + } + + //** Create the FCI context + psess->hfci = FCICreate( + &psess->erf, // error code return structure + fciFilePlaced, // callback for file placement notify + fciAlloc, + fciFree, + fciTempFile, + &ccab + ); + if (psess->hfci == NULL) { + mapFCIError(perr,psess,szFCI_CREATE,&psess->erf); + return FALSE; + } + } + return TRUE; +} /* ensureCabinet() */ + + +/*** ccabFromSession - Fill in a CCAB structure from a SESSION structure + * + * Entry: + * pccab - CCAB to fill in + * psess - Session to use + * cbPrevCab - Size of previous cabinet; 0 if no previous cabinet + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pccab updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL ccabFromSession(PCCAB pccab, PSESSION psess, ULONG cbPrevCab, PERROR perr) +{ + HVARIABLE hvar; + + AssertSess(psess); + +#ifndef REMOVE_CHICAGO_M6_HACK + //** Pass hack flag on to FCI + pccab->fFailOnIncompressible = psess->fFailOnIncompressible; +#endif + + /*** Switch to new disk, if necessary + * + * NOTE: cbPrevCab is an *estimate* from FCI. We use it to decide + * if we need to switch to a new disk. If we *don't* switch to + * a new disk, then our free space on disk value (psess->cbDiskLeft) + * will be slightly off until we get called back by FCI at + * our fciStatus() function with statusCabinet, at which point + * we can correct the amount of free space. + * + * Also, we save this estimated size *before* we determine if + * we are switching to a new disk, since newDiskIfNecessary() + * will clear psess->cbCabinetEstimate if a new disk is needed, + * to prevent fciStatus() from messing with psess->cbDiskLeft. + * + * See fciStatus() for more details. + */ + psess->cbCabinetEstimate = cbPrevCab; // Save estimated size first + if (!newDiskIfNecessary(psess,(long)cbPrevCab,FALSE,perr)) { + return FALSE; + } + + //** Construct new cabinet information + psess->iCabinet++; // New cabinet number + pccab->iCab = psess->iCabinet; // Set cabinet number for FCI + pccab->iDisk = psess->iDisk; // Set disk number for FCI + pccab->setID = psess->setID; // Carry over set ID + + //** Get cabinet file name + if (!getVarWithOverride(psess, + pccab->szCab, + sizeof(pccab->szCab), + pszPATTERN_VAR_CAB_NAME, + pszVAR_CAB_NAME, + psess->iCabinet, + pszDIA_CABINET, + perr)) { + return FALSE; // perr already filled in + } + + //** Get current disk output directory + Assert(sizeof(pccab->szCabPath) >= sizeof(psess->achCurrOutputDir)); + strcpy(pccab->szCabPath,psess->achCurrOutputDir); + + //** Set cabinet limits + hvar = VarFind(psess->hvlist,pszVAR_MAX_CABINET_SIZE,perr); + Assert(!perr->fError); // Must be defined + pccab->cb = VarGetLong(hvar); // Get maximum cabinet size + if ((pccab->cb == 0) || // Default is max disk size + (pccab->cb > (ULONG)psess->cbDiskLeft)) { // Disk size is smaller + pccab->cb = psess->cbDiskLeft; // Use space on disk as cabinet limit + } + + //** Set folder limits + hvar = VarFind(psess->hvlist,pszVAR_FOLDER_SIZE_THRESHOLD,perr); + Assert(!perr->fError); // FolderSizeThreshold must be defined + pccab->cbFolderThresh = VarGetLong(hvar); // Get disk size; + if (pccab->cbFolderThresh == 0) { // Use default value + pccab->cbFolderThresh = pccab->cb; // Use max cabinet size + } + + //** Get user-readable disk label + Assert(sizeof(pccab->szDisk) >= sizeof(psess->achCurrDiskLabel)); + strcpy(pccab->szDisk,psess->achCurrDiskLabel); + + //** Save away cabinet and disk info for INF + if (!infAddCabinet(psess, + psess->iCabinet, + psess->iDisk, + pccab->szCab, + perr)) { + return FALSE; + } + + //** Success + return TRUE; +} /* ccabFromSession() */ + + +long roundUp(long value, long chunk) +{ + return ((value + chunk - 1)/chunk)*chunk; +} + + +/*** newDiskIfNecessary - Determine if new disk is necessary, update Session + * + * This function is called in the following cases: + * (1) No files have been placed on any disks or in any cabinets, yet. + * ==> This function is used as the common place to initialize all + * the disk information; we increment the disk number (to 1), + * set the max disk size, etc. + * (2) FCI has called us back to get the next cabinet information. + * ==> FCI has limited the cabinet size to almost exactly the size + * we specified in the last CCAB structure we passed to FCI + * (it is not exact, because the cabinet file needs the cabinet + * file name and disk label name for this new cabinet in order + * to figure out the precise size!). So we use the cbConsume + * value to figure out if the current disk is full (the directive + * file could permit multiple cabinets per disk, though that is + * kind of obscure). + * (3) We are placing a file on a disk *outside* of a cabinet. + * ==> In this case we need to check the limits on files per disk + * (the root directory size limitation) and the bytes per disk. + * If we cannot fit the file on the disk, then we increment to + * the next disk, leaving some free space. + * + * Entry: + * psess - Session to update + * cbConsume - Size of cabinet/file to be placed on current disk + * Pass -1 to force a new disk. + * Pass 0 if no previous cabinet. + * fSubOnNewDisk - TRUE => subtract cbConsume from new disk (used + * when copying a file to a disk outside a cabinet). + * FALSE => don't subtract cbConsume from new disk (used + * when copying a file to a cabinet). + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated if necessary for new disk + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL newDiskIfNecessary(PSESSION psess, + long cbConsume, + BOOL fSubOnNewDisk, + PERROR perr) +{ + long cbCluster; + int cch; + long cMaxFiles; + HVARIABLE hvar; + char *pch; + + AssertSess(psess); + + //** Get cluster size to help decide if disk is full + hvar = VarFind(psess->hvlist,pszVAR_CLUSTER_SIZE,perr); + Assert(!perr->fError); // Must be defined + cbCluster = VarGetLong(hvar); // Get cluster size + psess->cbClusterCabEst = cbCluster; // Save cluster size for current disk + + //** Get limits on files per disk (outside of cabinets) + hvar = VarFind(psess->hvlist,pszVAR_MAX_DISK_FILE_COUNT,perr); + Assert(!perr->fError); // Must be defined + cMaxFiles = VarGetLong(hvar); // Get file count limit + + //** Figure out if we need to go to a new disk + if ((cbConsume == -1) || // Forced new disk + psess->fForceNewDisk || // .New Disk directive + (roundUp(cbConsume,cbCluster) > psess->cbDiskLeft) || // No more space + (!inCabinet(psess,perr) && (psess->cFilesInDisk >= cMaxFiles))) { + + psess->fForceNewDisk = FALSE; // Reset force flag + psess->iDisk++; // New disk + + //** Get max disk size for this disk + if (-1 == (psess->cbDiskLeft = getMaxDiskSize(psess,perr))) { + return FALSE; + } + + //** Update ClusterSize and MaxDiskFileCount (if standard disk size) + if (!setDiskParameters(psess,NULL,psess->cbDiskLeft,perr)) { + return FALSE; + } + + if (fSubOnNewDisk) { // Have to subtract from new disk + psess->cbDiskLeft -= roundUp(cbConsume,cbCluster); + } + psess->cFilesInDisk = 1; // Only one file on new disk so far + + //** Tell fciStatus() not to update psess->cbDiskLeft! + psess->cbCabinetEstimate = 0; + } + else { // Update space left on current disk + cbConsume = roundUp(cbConsume,cbCluster); + psess->cbDiskLeft -= cbConsume; // Update remaining space on disk + if (!inCabinet(psess,perr)) { // Not in a cabinet + psess->cFilesInDisk++; // Update count of files on disk + } + return TRUE; // Nothing more to do! + } + + //** OK, we have a new disk: + // 1) Create output directory + // 2) Get user-readable disk label + // 3) Add disk to INF file + + //** Get disk output directory name + if (!getVarWithOverride(psess, + psess->achCurrOutputDir, + sizeof(psess->achCurrOutputDir), + pszPATTERN_VAR_DISK_DIR, + pszVAR_DISK_DIR_NAME, + psess->iDisk, + pszDIA_DISK_DIR, + perr)) { + return FALSE; // perr already filled in + } + + //** Make sure destination directory exists + if (!ensureDirectory(psess->achCurrOutputDir,FALSE,perr)) { + return FALSE; // perr already filled in + } + + //** Append path separator if necessary + pch = psess->achCurrOutputDir; + cch = strlen(pch); + appendPathSeparator(&(pch[cch-1])); + + //**(2) Get the sticky, user-readable disk label + if (!getVarWithOverride(psess, + psess->achCurrDiskLabel, + sizeof(psess->achCurrDiskLabel), + pszPATTERN_VAR_DISK_LABEL, + pszVAR_DISK_LABEL_NAME, + psess->iDisk, + pszDIA_DISK_LABEL, + perr)) { + return FALSE; // perr already filled in + } + + //**(3) Add new disk to INF file + if (!infAddDisk(psess,psess->iDisk,psess->achCurrDiskLabel,perr)) { + return FALSE; + } + + return TRUE; +} /* newDiskIfNecessary() */ + + +/*** setCabinetReserve - Set reserved size fields in CCAB + * + * Entry: + * pccab - CCAB to fill in + * psess - Session to use + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pccab updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL setCabinetReserve(PCCAB pccab, PSESSION psess, PERROR perr) +{ + HVARIABLE hvar; + + hvar = VarFind(psess->hvlist,pszVAR_RESERVE_PER_CABINET,perr); + Assert(!perr->fError); // Must be defined + pccab->cbReserveCFHeader = (UINT)VarGetLong(hvar); + + hvar = VarFind(psess->hvlist,pszVAR_RESERVE_PER_FOLDER,perr); + Assert(!perr->fError); // Must be defined + pccab->cbReserveCFFolder = (UINT)VarGetLong(hvar); + + hvar = VarFind(psess->hvlist,pszVAR_RESERVE_PER_DATA_BLOCK,perr); + Assert(!perr->fError); // Must be defined + pccab->cbReserveCFData = (UINT)VarGetLong(hvar); + + return TRUE; +} /* setCabinetReserve() */ + + +/*** tcompFromSession - Get current compression setting + * + * Entry: + * psess - Session to update + * perr - ERROR structure + * + * Exit-Success: + * Returns one of the tcompXXX equates + * + * Exit-Failure: + * Returns tcompBAD; perr filled in + */ +int tcompFromSession(PSESSION psess, PERROR perr) +{ + HVARIABLE hvar; + int typeC; + int level; // Quantum compression level + int memory; // Quantum compression memory + + AssertSess(psess); + //** Get compression setting + hvar = VarFind(psess->hvlist,pszVAR_COMPRESS,perr); + Assert(!perr->fError); // Must be defined + if (VarGetBool(hvar)) { // Compression is on + //** Get the compression type + hvar = VarFind(psess->hvlist,pszVAR_COMPRESSION_TYPE,perr); + Assert(!ErrIsError(perr)); // Must be defined + typeC = CompTypeFromPSZ(VarGetString(hvar),perr); + Assert(!ErrIsError(perr)); // Checked when it was defined + switch (typeC) { + case tcompTYPE_MSZIP: + return tcompTYPE_MSZIP; + + case tcompTYPE_QUANTUM: +#ifdef BIT16 + //** Quantum *compression* not supported in 16-bit mode. + // We should never get here because the dfparse.c + // validation should generate an error if Quantum is + // selected. + Assert(0); +#endif + //** Get the compression level setting + hvar = VarFind(psess->hvlist,pszVAR_COMPRESSION_LEVEL,perr); + Assert(!ErrIsError(perr)); // Must be defined + level = CompLevelFromPSZ(VarGetString(hvar),perr); + Assert(!ErrIsError(perr)); // Checked when it was defined + + //** Get the compression memory setting + hvar = VarFind(psess->hvlist,pszVAR_COMPRESSION_MEMORY,perr); + Assert(!ErrIsError(perr)); // Must be defined + memory = CompMemoryFromPSZ(VarGetString(hvar),perr); + Assert(!ErrIsError(perr)); // Checked when it was defined + + //** Construct TCOMP and return it + return TCOMPfromTypeLevelMemory(typeC,level,memory); + } + //** Unknown compression type + Assert(0); + } + else { + return tcompTYPE_NONE; // Compression is off + } + Assert(0); +} /* tcompFromSession() */ + + +/*** checkDiskClusterSize - Check disk size and cluster size + * + * Make sure disk size is valid for cluster size. + * + * Entry: + * psess - Session to check + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL checkDiskClusterSize(PSESSION psess, PERROR perr) +{ + long cbCluster; + long cbDisk; + long cClusters; + HVARIABLE hvar; + + //** Get max disk size + if (-1 == (cbDisk = getMaxDiskSize(psess,perr))) { + return FALSE; + } + + //** Get cluster size + hvar = VarFind(psess->hvlist,pszVAR_CLUSTER_SIZE,perr); + Assert(!perr->fError); // Must be defined + cbCluster = VarGetLong(hvar); + + //** Make sure disk size is an exact multiple of the cluster size + cClusters = cbDisk / cbCluster; // Gets truncated to integer + + //** Return result + if (cbDisk == (cClusters*cbCluster)) { + return TRUE; + } + else { + //** Disk size is not a multiple of the cluster size + ErrSet(perr,pszDIAERR_DISK_CLUSTER_SIZE,"%ld%ld",cbDisk,cbCluster); + return FALSE; + } +} /* checkDiskClusterSize() */ + + +/*** getMaxDiskSize - Get current maximum disk size setting + * + * Entry: + * psess - Session + * perr - ERROR structure + * + * Exit-Success: + * Returns maximum disk size + * + * Exit-Failure: + * Returns -1; perr filled in + */ +long getMaxDiskSize(PSESSION psess, PERROR perr) +{ + long cb; + HVARIABLE hvar; + + //** Build variable name that *may* exist for this disk + // NOTE: During pass 1, and before newDiskIfNeccessary() is called, + // psess->iDisk will be 0, so we'll always get the MaxDiskSize + // variable value (unless someone happens to define MaxDiskSize0!) + // + if (!nameFromTemplate(psess->achMsg, + sizeof(psess->achMsg), + pszPATTERN_VAR_MAX_DISK_SIZE, + psess->iDisk, + pszVAR_MAX_DISK_SIZE, + perr)) { + Assert(0); // Should never fail + return FALSE; // perr already filled in + } + + //** See if this variable exists + hvar = VarFind(psess->hvlist,psess->achMsg,perr); + if (hvar != NULL) { // Yes, get its value + cb = VarGetLong(hvar); + } + else { // NO, no MaxDiskSizeN variable + ErrClear(perr); // Not an error + //** Use default variable + hvar = VarFind(psess->hvlist,pszVAR_MAX_DISK_SIZE,perr); + Assert(!perr->fError); // Must be defined + cb = VarGetLong(hvar); + } + + return cb; +} /* getMaxDiskSize() */ + + +/*** setDiskParameters - Set ClusterSize/MaxDiskFileCount + * + * If the specified disk size is on our predefined list, then set the + * standard values for ClusterSize and MaxDiskFileCount. + * + * Entry: + * psess - Session + * pszDiskSize - Disk size string (NULL if cbDisk should be used instead) + * cbDisk - Disk size (only if pszDiskSize == NULL) + * perr - ERROR structure + * + * Exit-Success: + * TRUE; ClusterSize/MaxDiskFileCount adjusted if specified size is + * on our special list. + * + * Exit-Failure: + * Returns -1; perr filled in + */ +BOOL setDiskParameters(PSESSION psess, + char *pszDiskSize, + long cbDisk, + PERROR perr) +{ + char achSize[50]; + ERROR errIgnore; + char *psz; + + //** Determine which parameter to use + if (pszDiskSize == NULL) { + _ltoa(cbDisk,achSize,10); // Convert number to string + psz = achSize; + } + else { + psz = pszDiskSize; + } + + //** Look up special list, get integer size for sure + if (!(cbDisk = IsSpecialDiskSize(psess,psz))) { + return TRUE; // Not a special size + } + + // NOTE: We have to be careful not to set these variables + // until they are defined! + if (VarFind(psess->hvlist,pszVAR_CLUSTER_SIZE,&errIgnore)) { + //** ClusterSize is defined + if (!VarSetLong(psess->hvlist,pszVAR_CLUSTER_SIZE,cbDisk,perr)) { + return FALSE; + } + } + if (VarFind(psess->hvlist,pszVAR_MAX_DISK_FILE_COUNT,&errIgnore)) { + //** MaxDiskFileCount is defined + if (!VarSetLong(psess->hvlist,pszVAR_MAX_DISK_FILE_COUNT,cbDisk,perr)) { + return FALSE; + } + } + return TRUE; +} /* setDiskParameters() */ + + +/*** fnofpDiamond - Override file date/time/attributes + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +FNOVERRIDEFILEPROPERTIES(fnofpDiamond) +{ + PFILEINFO pfinfo; + PSESSION psess; + + psess = (PSESSION)pv; + AssertSess(psess); + + //** Use override values and/or store real values + if (psess->fGenerateInf) { // Only valid if INF will be made + pfinfo = GLGetValue(psess->hgenFileLast); + AssertFinfo(pfinfo); + if (pfinfo->flags & fifDATE_SET) { + pfta->date = pfinfo->fta.date; // Override + } + else { + pfinfo->fta.date = pfta->date; // Store for INF generation + } + + if (pfinfo->flags & fifTIME_SET) { + pfta->time = pfinfo->fta.time; // Override + } + else { + pfinfo->fta.time = pfta->time; // Store for INF generation + } + + if (pfinfo->flags & fifATTR_SET) { + pfta->attr = pfinfo->fta.attr; // Override + } + else { + pfinfo->fta.attr = pfta->attr; // Store for INF generation + } + } + + //** Success + return TRUE; +} /* fnofpDiamond() */ + + +/*** fciOpenInfo - Get file date, time, and attributes for FCI + * + * Entry: + * pszName - filespec + * pdate - buffer to receive date + * ptime - buffer to receive time + * pattribs - buffer to receive attributes + * pv - our context pointer + * + * Exit-Success: + * Returns file handle; *pdate, *ptime, *pattribs filled in. + * + * Exit-Failure: + * Returns -1; + */ +FNFCIGETOPENINFO(fciOpenInfo) +{ + FILETIMEATTR fta; + int hf; + PERROR perr = ((PSESSIONANDERROR)pv)->perr; + PSESSION psess = ((PSESSIONANDERROR)pv)->psess; + + AssertSess(psess); + + //** Get real file date, time, and attributes + if (!GetFileTimeAndAttr(&fta,pszName,perr)) { + return FALSE; + } + + //** Get/Override file date/time/attributes + if (!fnofpDiamond(&fta,psess,perr)) { + return FALSE; // perr already filled in + } + + //** Tell FCI what to use + *pdate = fta.date; + *ptime = fta.time; + *pattribs = fta.attr; + + //** Open the file + hf = _open(pszName, _O_RDONLY | _O_BINARY); + if (hf == -1) { + ErrSet(perr,pszDIAERR_OPEN_FAILED,"%s",pszName); + return -1; // abort on error + } + return hf; +} + + +/*** fciFilePlaced - FCI calls this when a file is commited to a cabinet + * + * Entry: + * pccab - Current cabinet structure + * pszFile - Name of file placed in this cabinet + * cbFile - File size + * fContinuation - TRUE => Continued from previous cabinet + * pv - Really a psae + * + * Exit-Success: + * returns anything but -1; + * + * Exit-Failure: + * returns -1; + */ +FNFCIFILEPLACED(fciFilePlaced) +{ + PSESSION psess = ((PSESSIONANDERROR)pv)->psess; + PERROR perr = ((PSESSIONANDERROR)pv)->perr; + + AssertSess(psess); + + if (psess->levelVerbose >= vbMORE) { + if (fContinuation) { + MsgSet(psess->achMsg,pszDIA_FILE_IN_CAB_CONT,"%s%s%d%s", + pszFile, pccab->szCab, pccab->iCab, pccab->szDisk); + } + else { + MsgSet(psess->achMsg,pszDIA_FILE_IN_CAB,"%s%s%d%s", + pszFile, pccab->szCab, pccab->iCab, pccab->szDisk); + } + lineOut(psess,psess->achMsg,TRUE); + } + + //** Add file entry to INF temp file + if (psess->fGenerateInf) { // Only if generating INF file + if (!fContinuation) { // Only if not a continuation + if (!modeInfAddFile(psess, + pszFile, + pccab->iDisk, + pccab->iCab, + perr)) { + return -1; // Abort with error + } + } + } + + return 0; // Success +} /* fciFilePlaced() */ + + +/*** fciGetNextCabinet - FCI calls this to get new cabinet info + * + * NOTE: See FCI.H for more details. + * + * Entry: + * pccab - Points to previous current-cabinet structure + * cbPrevCab - Size of previous cabinet - ESTIMATE! + * pv - Really a psae + * + * Exit-Success: + * Returns TRUE; pccab updated with info for new cabinet + * + * Exit-Failure: + * returns FALSE; ((psae)pv)->perr filled in + */ +FNFCIGETNEXTCABINET(fciGetNextCabinet) +{ + PSESSION psess = ((PSESSIONANDERROR)pv)->psess; + PERROR perr = ((PSESSIONANDERROR)pv)->perr; + + //** Set CCAB for new cabinet (and maybe disk) + AssertSess(psess); + if (!ccabFromSession(pccab,psess,cbPrevCab,perr)) { + return FALSE; + } + + //** Current folder and cabinet are empty + psess->cFilesInFolder = 0; + psess->cFilesInCabinet = 0; + + //** Success + return TRUE; +} /* fciGetNextCabinet() */ + + +/*** fciStatus - FCI calls this periodically when adding files + * + * NOTE: See FCI.H for more details. + * + * Entry: + * typeStatus - Type of status call being made + * cb1 - Size of compressed data generated since last call + * cb2 - Size of uncompressed data processed since last call + * pv - Really a psae + * + * Exit-Success: + * returns anything but -1, continue building cabinets + * + * Exit-Failure: + * returns -1, FCI should abort + */ +FNFCISTATUS(fciStatus) +{ + long cbCabinetActual; + double percent; + PERROR perr = ((PSESSIONANDERROR)pv)->perr; + PSESSION psess = ((PSESSIONANDERROR)pv)->psess; + + AssertSess(psess); + switch (typeStatus) { + + case statusFolder: + //** Adding folder to cabinet (i.e., folder flush) + psess->cFilesInFolder = 0; // reset count + return TRUE; // Continue + + case statusFile: + //** Compressing a file + psess->cbFileBytes += cb2; // Update progress data + psess->cbFileBytesComp += cb1; + + //** Compute percentage complete + if (psess->cbTotalFileBytes > 0) { + percent = psess->cbFileBytes/(float)psess->cbTotalFileBytes; + percent *= 100.0; // Make it 0..100 + } + else { + Assert(0); // Should never get here + percent = 0.0; + } + + //** Amount of status depends upon verbosity + if (psess->levelVerbose >= vbFULL) { + MsgSet(psess->achMsg,pszDIA_PERCENT_COMPLETE_DETAILS, + "%6.2f%,ld%,ld", + percent,psess->cbFileBytes,psess->cbFileBytesComp); + lineOut(psess,psess->achMsg,TRUE); + } + else { + MsgSet(psess->achMsg,pszDIA_PERCENT_COMPLETE_SOME, + "%6.2f%s%,ld%,ld", + percent, psess->achCurrFile, psess->iCurrFile, psess->cFiles); + //** NOTE: No line-feed, so that we don't scroll + lineOut(psess,psess->achMsg,FALSE); + } + return TRUE; // Continue + + case statusCabinet: + /** Cabinet completed and written to disk + * + * If this cabinet did not force a disk change, then we need to + * correct the amount of free space left on the current disk, + * since FCI only passed us an *estimated* cabinet size when it + * called our fciGetNextCabinet() function! + * + * Why did FCI estimate the cabinet size, you ask? Because there + * is a "chicken-and-egg" situation between FCI and Diamond! Except + * for the last cabinet in a set of cabinets, each cabinet file + * has a *forward* reference to the next cabinet. FCI calls our + * fciGetNextCabinet() function to get the cabinet file name and + * disk name for this forward reference, and passes the *estimated* + * cabinet size of the current cabinet, because Diamond needs this + * information to decide if it has to put the next cabinet on a new + * disk. + * + * If Diamond decides to put the next cabinet on a new disk, then + * everything is fine, and the fact that Diamond had an estimate + * of the cabinet size is irrelevant. BUT, if more cabinets or + * files are being placed on the same disk, then Diamond needs an + * *exact* cabinet size so that the disk free space can be precise. + * + * So, FCI calls us back with the original estimate and the final + * size, and we adjust our disk free space amount as necessary. + * + * We also return to FCI the *rounded* cabinet size, so that it + * can adjust its internal maximum cabinet size, too! + */ + if (psess->cbCabinetEstimate != 0) { + //** Round up to cabinet size on disk + cbCabinetActual = roundUp(cb2,psess->cbClusterCabEst); + + //** Need to add back old estimate and subtract actual size + Assert(psess->cbCabinetEstimate == (long)cb1); + psess->cbDiskLeft += roundUp(cb1,psess->cbClusterCabEst); + psess->cbDiskLeft -= roundUp(cb2,psess->cbClusterCabEst); + return cbCabinetActual; // Tell FCI size we actually used + } + return cb2; // Let FCI use size it had + + default: + //** Unknown status callback + Assert(0); + return TRUE; + } +} /* fciStatus() */ + + +/*** fciAlloc - memory allocator for FCI + * + * Entry: + * cb - size of block to allocate + * + * Exit-Success: + * returns non-NULL pointer to block of size at least cb. + * + * Exit-Failure: + * returns NULL + */ +FNALLOC(fciAlloc) +{ +#ifdef BIT16 + //** Use 16-bit function + return _halloc(cb,1); +#else // !BIT16 + //** Use 32-bit function + return malloc(cb); +#endif // !BIT16 +} /* fciAlloc() */ + + +/*** fciFree - memory free function for FCI + * + * Entry: + * pv - memory allocated by fciAlloc to be freed + * + * Exit: + * Frees memory + */ +FNFREE(fciFree) +{ +#ifdef BIT16 + //** Use 16-bit function + _hfree(pv); +#else // !BIT16 + //** Use 32-bit function + free(pv); +#endif // !BIT16 +} + + +/*** fciTempFile - Construct tempfile name for FCI + * + * Entry: + * pszTempName - Buffer to be filled in with tempfile name + * cbTempName - Size of tempfile name buffer + * + * Exit-Success: + * Returns TRUE; psz filled in + * + * Exit-Failure: + * Returns FALSE; + */ +FNFCIGETTEMPFILE(fciTempFile) +{ + char *psz; + + psz = _tempnam("","dc"); // Get a name + if ((psz != NULL) && (strlen(psz) < (unsigned)cbTempName)) { + strcpy(pszTempName,psz); // Copy to caller's buffer + free(psz); // Free temporary name buffer + return TRUE; // Success + } + //** Failed + if (psz) { + free(psz); + } + return FALSE; +} + + +/*** getCompressedFileName - Generate compressed filename + * + * Entry: + * psess - Session (has variable list) + * pszResult - Buffer to receive concatentation + * cbResult - Size of pszResult + * pszSrc - Filespec to parse + * perr - ERROR structure to fill in + * + * Exit-Success: + * Returns TRUE; pszResult buffer has generated name + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * Notes: + * (1) Takes pszSrc filespec, trims off path, and replaces or appends + * the value of CompressedFileExtensionChar in the extension. + * + * Examples: + * (1) "foo" ,"_" => "foo._" + * (2) "foo.b" ,"_" => "foo.b_" + * (3) "foo.ba" ,"_" => "foo.ba_" + * (4) "foo.bar","_" => "foo.ba_" + */ +BOOL getCompressedFileName(PSESSION psess, + char * pszResult, + int cbResult, + char * pszSrc, + PERROR perr) +{ + int cch; + char chTrail; // Trailing character to use + HVARIABLE hvar; + int i; + char *pch; + char *pchStart; + + AssertSess(psess); + + //** Get trailing character + hvar = VarFind(psess->hvlist,pszVAR_COMP_FILE_EXT_CHAR,perr); + Assert(!perr->fError); // Must be defined + pch = VarGetString(hvar); // Get trailing string + chTrail = *pch; // Take first char + + //** Find name.ext + pchStart = getJustFileNameAndExt(pszSrc, perr); + if (pchStart == NULL) { + return FALSE; // perr already filled in + } + + //** Make sure name is not too long + cch = strlen(pchStart); // Length of name.ext + if (cch >= cbResult) { + ErrSet(perr,pszDIAERR_PATH_TOO_LONG,"%s",pszSrc); + return FALSE; + } + + //** Find last extension separator + strcpy(pszResult,pchStart); // Copy name to output buffer + pchStart = pszResult + cch; // Assume there is no extension ("foo") + for (pch=pszResult; *pch; pch++) { + if ((*pch == chNAME_EXT_SEP) && // Got one "." + (*(pch+1) != chNAME_EXT_SEP) && // Ignore ".." + (*(pch+1) != chPATH_SEP1) && // Ignore ".\" + (*(pch+1) != chPATH_SEP2)) { // Ignore "./" + pchStart = pch+1; // Ext starts after separator + } + } + + //** Increase length if not full extension (need room for '_' character) + if (strlen(pchStart) < 3) { + cch++; + } + + //** Edit result buffer + if (*pchStart == '\0') { // Extension is empty or missing + if (*(pchStart-1) != chNAME_EXT_SEP) { // Need to add "." + *pchStart++ = chNAME_EXT_SEP; + cch++; // Account for added "." + } + //** Add trail character below + } + else { // Extension has at least one character + //** skip to location to place new character + for (i=0; (i<2) && *pchStart; i++) { + pchStart++; + } + } + + //** Check for buffer overflow + if (cch >= cbResult) { + ErrSet(perr,pszDIAERR_PATH_TOO_LONG,"%s",pszSrc); + return FALSE; + } + + //** Finally, store trailing character + *pchStart++ = chTrail; // Store trailing character + *pchStart++ = '\0'; // Terminate filename + + //** Success + return TRUE; +} /* getJustFileNameAndExt() */ + + +/*** resetSession - Reset SESSION members for start of a new pass + * + * Entry: + * psess - Description of operation to perform + * + * Exit: + * Initializes per-pass members. + */ +void resetSession(PSESSION psess) +{ + AssertSess(psess); + psess->act = actBAD; + psess->iDisk = 0; + psess->iCabinet = 0; + psess->iFolder = 0; + psess->cbDiskLeft = -1; // Force new disk first time + psess->cErrors = 0; + psess->cWarnings = 0; + psess->cbFileBytes = 0; + psess->cbFileBytesComp = 0; + psess->iCurrFile = 0; + psess->fRunSeen = FALSE; + + psess->cFilesInFolder = 0; + psess->cFilesInCabinet = 0; + psess->cFilesInDisk = 0; + psess->cbCabinetEstimate = 0; // No estimated cabinet size + psess->ddfmode = ddfmodeUNKNOWN; // Don't know unified vs. + // relational, yet + psess->fExpectFileCommand = TRUE; // Default is file copy commands + psess->fCopyToInf = FALSE; // Not in .InfBegin/End + psess->hgenFile = (HGENERIC)-1; // Not valid + psess->hgenFileLast = (HGENERIC)-1; // Not valid +} /* resetSession() */ + + +/*** parseCommandLine - Parse the command line arguments + * + * Entry: + * cArg - Count of arguments, including program name + * apszArg - Array of argument strings + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess filled in. + * + * Exit-Failure: + * Returns actBAD; perr filled in with error. + */ +BOOL parseCommandLine(PSESSION psess, int cArg, char *apszArg[], PERROR perr) +{ + ACTION act=actBAD; // No action determined, yet + char *apszFile[2]; // Non-directive file names + int cFile=0; // Count of non-directive file names seen + int i; + BOOL fDefine=FALSE; // TRUE => Last arg was /D + BOOL fFile=FALSE; // TRUE => Last arg was /F + BOOL fLocation=FALSE;// TRUE => Last arg was /L + HFILESPEC hfspec; + + AssertSess(psess); + + //** Parse args, skipping program name + for (i=1; i<cArg; i++) { + if ((apszArg[i][0] == chSWITCH1) || + (apszArg[i][0] == chSWITCH2) ) { + //** Have a switch to parse, make sure switch is OK here + if (fDefine) { + ErrSet(perr,pszDIAERR_MISSING_VAR_DEFINE); + return FALSE; + } + if (fFile) { + ErrSet(perr,pszDIAERR_MISSING_FILE_NAME); + return FALSE; + } + if (fLocation) { + ErrSet(perr,pszDIAERR_MISSING_LOCATION); + return FALSE; + } + + switch (toupper(apszArg[i][1])) { + case chSWITCH_HELP: + if (apszArg[i][2] != '\0') { + ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + } + //** Ignore any remaining parameters + psess->act = actHELP; // Show help + return TRUE; + break; + +#ifndef REMOVE_CHICAGO_M6_HACK + case chSWITCH_ANDY: + psess->fFailOnIncompressible = TRUE; + break; +#endif + + case chSWITCH_DEFINE: + if (apszArg[i][2] != '\0') { + ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + } + else if ((act != actBAD) && (act != actDIRECTIVE)) { + //* Got /D when we are not doing /F processing + ErrSet(perr,pszDIAERR_SWITCH_NOT_EXPECTED,"%s",apszArg[i]); + return FALSE; + } + fDefine = TRUE; + break; + + case chSWITCH_FILE: + if (apszArg[i][2] != '\0') { + ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + } + else if ((act != actBAD) && (act != actDIRECTIVE)) { + //* Got /F after we decided we're not doing /F + ErrSet(perr,pszDIAERR_SWITCH_NOT_EXPECTED,"%s",apszArg[i]); + return FALSE; + } + //* Next parm should be a directive file name + act = actDIRECTIVE; // The die is cast... + fFile = TRUE; + break; + + case chSWITCH_LOCATION: + if (apszArg[i][2] != '\0') { + ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + } + //* Next parm should be a location + fLocation = TRUE; + break; + +#ifdef ASSERT + case chSWITCH_MEMORY_DBG: + //** Turn on full memory heap checking (very slow!) + MemSetCheckHeap(TRUE); + break; +#endif + + case chSWITCH_VERBOSE: + if (apszArg[i][2] != '\0') { + psess->levelVerbose = atoi(&apszArg[i][2]); + } + else { + psess->levelVerbose = vbFULL; // Default to FULL + } + break; + + default: + ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + break; + } + } + //** Not a command line switch + else if (fFile) { + //** Grab a directive file + if (!addDirectiveFile(psess,apszArg[i],perr)) { + //** Error adding directive file; perr already set + return FALSE; // Failure + } + fFile = FALSE; // Done eating directive file + } + else if (fDefine) { + //** Grab a define + if (!addCmdLineVar(psess,apszArg[i],perr)) { + //** Error adding definition; perr already set + return FALSE; // Failure + } + fDefine = FALSE; // Done eating definition + } + else if (fLocation) { + //** Grab the location + if (strlen(apszArg[i]) >= sizeof(psess->achCurrOutputDir)) { + ErrSet(perr,pszDIAERR_LOCATION_TOO_LONG,"%s",apszArg[i]); + return FALSE; + } + strcpy(psess->achCurrOutputDir,apszArg[i]); + //** Make sure destination directory exists + if (!ensureDirectory(psess->achCurrOutputDir,FALSE,perr)) { + return FALSE; // perr already filled in + } + fLocation = FALSE; // Done eating location + } + else { + //** Should be a file name; + // Make sure we haven't made up our mind, yet! + if ((act != actBAD) && (act != actFILE)) { + //** Not doing single file compress, so this is a bad arg + ErrSet(perr,pszDIAERR_BAD_PARAMETER,"%s",apszArg[i]); + return FALSE; + } + act = actFILE; // The die is cast... + + //** Make sure we haven't seen too many file names + cFile++; // Count number of files we've seen + if (cFile > 2) { + //** Two many file names + ErrSet(perr,pszDIAERR_TWO_MANY_PARMS,"%s",apszArg[i]); + return FALSE; + } + + //** Store file name + apszFile[cFile-1] = apszArg[i]; + } + } + + //** Make sure no trailing /D or /F + if (fDefine) { + ErrSet(perr,pszDIAERR_MISSING_VAR_DEFINE); + return FALSE; + } + if (fFile) { + ErrSet(perr,pszDIAERR_MISSING_FILE_NAME); + return FALSE; + } + if (fLocation) { + ErrSet(perr,pszDIAERR_MISSING_LOCATION); + return FALSE; + } + + //** Update Session + switch (act) { + case actBAD: + case actHELP: + psess->act = actHELP; // If no args, show help + break; + + case actDIRECTIVE: + psess->act = act; + break; + + case actFILE: + psess->act = act; + //** Add source file specification + if (!(hfspec = addDirectiveFile(psess,apszFile[0],perr))) { + //** Error adding source file; perr already set + return FALSE; // Failure + } + + if (cFile == 2) { // Destination filename specified + if (!FLSetDestination(hfspec,apszFile[1],perr)) { + //** Error setting destination file; perr already set + return FALSE; // Failure + } + } + break; + } + + //** Success + return TRUE; +} + + +/*** addDirectiveFile - Add directive file to session list + * + * Entry: + * psess - Session to update + * pszArg - File name to add + * perr - ERROR structure + * + * Exit-Success: + * Returns HFILESPEC; psess updated. + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HFILESPEC addDirectiveFile(PSESSION psess, char *pszArg, PERROR perr) +{ + HFILESPEC hfspec; + + //** Make sure file exists + if (getFileSize(pszArg,perr) == -1) { + return NULL; // perr already filled in + } + + AssertSess(psess); + //** Make sure a list exists + if (psess->hflistDirectives == NULL) { + if (!(psess->hflistDirectives = FLCreateList(perr))) { + return FALSE; + } + } + + //** Add file to list + if (!(hfspec = FLAddFile(psess->hflistDirectives, pszArg, NULL, perr))) { + return NULL; + } + + //** Success + return hfspec; +} /* addDirectiveFile */ + + +/*** addCmdLineVar - Add a command line variable to session list + * + * Entry: + * psess - Session to update + * pszArg - Variable name=value to add + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; psess updated. + * + * Exit-Failure: + * Returns actBAD; perr filled in with error. + */ +BOOL addCmdLineVar(PSESSION psess, char *pszArg, PERROR perr) +{ + COMMAND cmd; // For var name & value + BOOL f; + + //** Make sure asserts work + SetAssertSignature((&cmd),sigCOMMAND); + + //** Parse assignment statment + if (!DFPParseVarAssignment(&cmd,psess,pszArg,perr)) { + return FALSE; + } + + //** Assign variable + f = setVariable(psess, + cmd.set.achVarName, + cmd.set.achValue, + perr); + + //** Clear signature + ClearAssertSignature((&cmd)); + + //** Return result + return f; +} /* addCmdLineVar() */ + + +#ifdef ASSERT +/*** fnafReport - Report assertion failure + * + * NOTE: See asrt.h for entry/exit conditions. + */ +FNASSERTFAILURE(fnafReport) +{ + //** Make sure we don't overwrite a status line! + printf("\n%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg); + exit(1); +} +#endif // ASSERT + + +/*** printError - Display error on stdout + * + * Entry + * perr - ERROR structure to print + * + * Exit-Success + * Writes error message to stdout. + */ +void printError(PSESSION psess, PERROR perr) +{ + //** Make sure error starts on a new line + if (psess->fNoLineFeed) { + printf("\n"); + psess->fNoLineFeed = FALSE; + } + + //** Determine type of error + if (perr->pszFile != NULL) { + //** Error in a directive file + printf("%s(%d): %s: %s\n", + perr->pszFile,perr->iLine,pszDIAERR_ERROR,perr->ach); + } + else { + //** General error + printf("%s: %s\n",pszDIAERR_ERROR,perr->ach); + } + + //** Count errors, to determine exit code and early out on MaxErrors + psess->cErrors++; // Count this error +} + + +/*** mapFCIError - Create error message from FCI error codes + * + * Entry: + * perr - ERROR structure to recieve message + * psess - Our context + * pszCall - FCI call that failed + * perf - FCI error structure + * + * Exit: + * perr filled in with formatted message + */ +void mapFCIError(PERROR perr, PSESSION psess, char *pszCall, PERF perf) +{ + char *pszErrno; + + pszErrno = mapCRTerrno(perf->erfType); // Get mapping, just in case + + switch (perf->erfOper) { + + case FCIERR_NONE: + Assert(0); + break; + + case FCIERR_ALLOC_FAIL: + ErrSet(perr,pszFCIERR_ALLOC_FAIL,"%s",pszCall); + break; + + case FCIERR_BAD_COMPR_TYPE: + ErrSet(perr,pszFCIERR_BAD_COMPR_TYPE,"%s",pszCall); + break; + + case FCIERR_MCI_FAIL: + ErrSet(perr,pszFCIERR_MCI_FAIL,"%s%s",pszCall,psess->achCurrFile); + break; + + case FCIERR_USER_ABORT: + ErrSet(perr,pszFCIERR_USER_ABORT,"%s",pszCall); + break; + + case FCIERR_OPEN_SRC: + ErrSet(perr,pszFCIERR_OPEN_SRC,"%s%s%s", + pszCall,psess->achCurrFile,pszErrno); + break; + + case FCIERR_READ_SRC: + ErrSet(perr,pszFCIERR_READ_SRC,"%s%s%s", + pszCall,psess->achCurrFile,pszErrno); + break; + + case FCIERR_TEMP_FILE: + ErrSet(perr,pszFCIERR_TEMP_FILE,"%s%s",pszCall,pszErrno); + break; + + case FCIERR_CAB_FILE: + ErrSet(perr,pszFCIERR_CAB_FILE,"%s%s",pszCall,pszErrno); + break; + + case FCIERR_M6_HACK_INCOMPRESSIBLE: + ErrSet(perr,pszFCIERR_M6_HACK_INCOMPRESSIBLE,"%s%s", + pszCall,psess->achCurrFile); + break; + + default: + ErrSet(perr,pszFCIERR_UNKNOWN_ERROR,"%s%d",pszCall,perf->erfOper); + break; + } +} /* mapFCIError() */ + + +/*** mapCRTerrno - Get error string from C run-time library errno + * + * Entry: + * errno - C run-time library errno value. + * + * Exit: + * Returns pointer to appropriate causation string. + */ +char *mapCRTerrno(int errno) +{ + switch (errno) { + case ECHILD: return pszCRTERRNO_ECHILD; + case EAGAIN: return pszCRTERRNO_EAGAIN; + case E2BIG: return pszCRTERRNO_E2BIG; + case EACCES: return pszCRTERRNO_EACCES; + case EBADF: return pszCRTERRNO_EBADF; + case EDEADLOCK: return pszCRTERRNO_EDEADLOCK; + case EDOM: return pszCRTERRNO_EDOM; + case EEXIST: return pszCRTERRNO_EEXIST; + case EINVAL: return pszCRTERRNO_EINVAL; + case EMFILE: return pszCRTERRNO_EMFILE; + case ENOENT: return pszCRTERRNO_ENOENT; + case ENOEXEC: return pszCRTERRNO_ENOEXEC; + case ENOMEM: return pszCRTERRNO_ENOMEM; + case ENOSPC: return pszCRTERRNO_ENOSPC; + case ERANGE: return pszCRTERRNO_ERANGE; + case EXDEV: return pszCRTERRNO_EXDEV; + default: return pszCRTERRNO_UNKNOWN; + } + Assert(0); + return NULL; +} /* mapCRTerrno() */ + + +/*** updateHgenLast - Set correct psess->hgenLast + * + * Entry: + * psess - Session + * pszDst - Destination file name + * + * Exit: + * psess->hgenFileLast set to point to current file + */ +void updateHgenLast(PSESSION psess, char *pszDst) +{ + PFILEINFO pfinfo; + + Assert(psess->hgenFileLast != NULL); // Catch read off end of list + if (psess->hgenFileLast == (HGENERIC)-1) { // Get first file on list + psess->hgenFileLast = GLFirstItem(psess->hglistFiles); + } + else { // Get next file + psess->hgenFileLast = GLNextItem(psess->hgenFileLast); + } + + //** Make sure we got the expected entry + pfinfo = GLGetValue(psess->hgenFileLast); + AssertFinfo(pfinfo); + Assert(strcmp(pszDst,GLGetKey(psess->hgenFileLast)) == 0); +} /* updateHgenLast() */ + + +/*** modeInfAddFile - Add a file line to INF, depending upon DDF mode + * + * There are two cases to consider: + * (1) "relational" mode + * ==> Augment psess->hglistFiles with placement information + * (2) "unified" mode + * ==> Augment psess->hglistFiles with placement information and + * then format and write out file line to INF file. + * Entry: + * psess - Session + * inf - Area of INF file to receive line + * pszFile - Destination file name + * iDisk - Disk number (1-based) + * iCabinet - Cabinet number (1-based, 0=> not in cabinet) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; line added to INF file + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL modeInfAddFile(PSESSION psess, + char *pszFile, + int iDisk, + int iCabinet, + PERROR perr) + +{ + HGENERIC hgen; + PFILEINFO pfinfo; + PFILEPARM pfparm; + + AssertSess(psess); + Assert(psess->ddfmode != ddfmodeBAD); + Assert(psess->ddfmode != ddfmodeUNKNOWN); + + //** Get file item + Assert(psess->hgenFile != NULL); // Catch read off end of list + if (psess->hgenFile == (HGENERIC)-1) { // Get first file on list + psess->hgenFile = GLFirstItem(psess->hglistFiles); + } + hgen = psess->hgenFile; + Assert(hgen != NULL); + psess->hgenFile = GLNextItem(hgen); // Advance for next time + + //** Get fileinfo and augment it + pfinfo = GLGetValue(hgen); + AssertFinfo(pfinfo); + Assert(strcmp(pszFile,GLGetKey(hgen)) == 0); // Verify destination name + pfinfo->iDisk = iDisk; // Store placement info + pfinfo->iCabinet = iCabinet; // Store placement info + + //** Let /SIZE parm override true file size if former is *larger* + // NOTE: This is only for reporting in the INF file -- this has + // no affect on space used on the disk! + pfparm = GLFindAndGetValue(pfinfo->hglistParm,pszPARM_FILESIZE); + if (pfparm) { // /Size specfied + AssertFparm(pfparm); + pfinfo->cbFile = atol(pfparm->pszValue); + } + + //** Write INF line if in unified mode + if (psess->ddfmode == ddfmodeUNIFIED) { + if (!infAddFile(psess,pszFile,pfinfo,pfinfo->hglistParm,perr)) { + return FALSE; // perr already filled in + } + } + + //** Success + return TRUE; +} /* modeInfAddFile() */ + + +/*** modeInfAddLine - Add line to INF, depending upon DDF mode + * + * There are several cases to consider: + * (1) The disk or cabinet area is specified + * ==> Write line immediately to INF + * (2) "relational" mode AND GenerateINF is TRUE + * ==> Write line immediately to INF + * (3) "relational" mode AND GenerateINF is FALSE AND File area specified + * => ERROR -- it is unclear what the semantics of this should + * be, since the INF is not being generated in step with the + * file copy commands. So writes to the FILE area are not + * supported in the "layout" section of the DDF. + * (4) "unified" mode AND File area specified + * (a) No file copy commands have been processed + * ==> Write line immediately to INF + * (b) One or more file copy commands already done + * ==> This is the tricky case, because we need to make sure that + * the line is written to the INF in correct synchronization + * with surrounding file copy commands. If files are being + * placed in cabinets, then we may have passed file copy + * commands to FCI, but it may not yet have actually placed them + * in cabinets, so we won't have written their lines to the INF + * file! So, if any "unmatched" files are queued up, we append + * this INF to the last file, and this line (or lines) will be + * written when this file is actually added to the INF. If + * there are no queued files (we might not be in a cabinet, or + * the cabinet may have flushed), then we write to the INF + * immediately. + * (5) "unknown" mode (haven't decided between relational or unified) + * ==> Write line immediately to INF + * + * Entry: + * psess - Session + * inf - Area of INF file to receive line + * pszLine - Line to add + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; line added to INF file + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL modeInfAddLine(PSESSION psess, + INFAREA inf, + char *pszLine, + PERROR perr) +{ + BOOL fGenerateInf; + HVARIABLE hvar; + PFILEINFO pfinfo; + PLINEINFO plinfo; + + AssertSess(psess); + hvar = VarFind(psess->hvlist,pszVAR_GENERATE_INF,perr); + Assert(!perr->fError); // Must be defined + fGenerateInf = VarGetBool(hvar); // Get INF generation setting + +/* + * Work common to Pass 1 & 2 + */ + + //** Check for invalid location of InfWrite FILE + if ((psess->ddfmode == ddfmodeRELATIONAL) && + !fGenerateInf && + (inf == infFILE)) { + //** InfWrite to file area in relation layout section of DDF + Assert(psess->fExpectFileCommand); + ErrSet(perr,pszDIAERR_INF_IN_LAYOUT); + return FALSE; + } + + //** If this is pass 1, we're done + if (!psess->fPass2) { + return TRUE; + } + +/* + * Pass 2 + */ + + //** Check for InfWrite FILE that must be postponed + if ((psess->ddfmode == ddfmodeUNIFIED) && + (inf == infFILE) && + (psess->iCurrFile > 0)) { + //** InfWrite to file area while doing file copy commands + Assert(fGenerateInf); // Must be generating INF + pfinfo = GLGetValue(psess->hgenFileLast); // Get last file added + AssertFinfo(pfinfo); + + //** Do not have to postpone if preceding file written to INF already + if (pfinfo->flags & fifWRITTEN_TO_INF) { + return infAddLine(psess->hinf,inf,pszLine,perr); + } + + //** Postpone write + if (pfinfo->hglistInfLines == NULL) { // Create list + pfinfo->hglistInfLines = GLCreateList(NULL, + DestroyLineInfo, + pszDIA_LINE_INFO, + perr); + if (!pfinfo->hglistInfLines) { + return FALSE; // perr already filled in + } + } + + //** Create line info + if (!(plinfo = MemAlloc(sizeof(LINEINFO)))) { + ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_LINES); + return FALSE; + } + if (!(plinfo->pszLine = MemStrDup(pszLine))) { + ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_LINES); + MemFree(plinfo); + return FALSE; + } + + //** Add line to list + if (!GLAdd(pfinfo->hglistInfLines, // List + NULL, // key name + plinfo, // file info + pszDIA_LINE_INFO, // Description for error message + FALSE, // Uniqueness setting + perr)) { + MemFree(plinfo->pszLine); + MemFree(plinfo); + return FALSE; + } + + //** Initialize remainder of structure + plinfo->inf = inf; + SetAssertSignature(plinfo,sigLINEINFO); + return TRUE; + } + + //** We're OK to write the line right now! + return infAddLine(psess->hinf,inf,pszLine,perr); +} /* modeInfAddLine() */ + + +/*** addFileToSession - Add file to session list + * + * Entry: + * psess - Session to update + * pszSrc - Source file name + * pszDst - Destination file name (used as key in list) + * cbFile - Size of file + * pcmd - Command to process (ct == ctFILE) + * perr - ERROR structure + * + * Exit-Success: + * Returns HGENERIC; file added to psess->hglistFiles; pcmd->file.hglist + * moved to newly added FILEINFO entry! + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HGENERIC addFileToSession(PSESSION psess, + char *pszSrc, + char *pszDst, + long cbFile, + PCOMMAND pcmd, + PERROR perr) +{ + BOOL fUnique; // True if file name has to be unique + HGENERIC hgen; + HVARIABLE hvar; + PFILEINFO pfinfo; + PFILEINFO pfinfoFirst; + PFILEPARM pfparm; + + AssertSess(psess); + Assert(pcmd->ct == ctFILE); + + //** Get UniqueFiles setting + hvar = VarFind(psess->hvlist,pszVAR_UNIQUE_FILES,perr); + Assert(!perr->fError); // Must be defined + fUnique = VarGetBool(hvar); // Get default setting + + //** See if default is overridden on command line + pfparm = GLFindAndGetValue(pcmd->file.hglist,pszFILE_UNIQUE); + if (pfparm) { // /Unique specfied + AssertFparm(pfparm); + if (-1 == (fUnique = BOOLfromPSZ(pfparm->pszValue,perr))) { + return NULL; // perr filled in already + } + } + + //** Relational mode requires unique destination file names + if ((psess->ddfmode == ddfmodeRELATIONAL) && !fUnique) { + ErrSet(perr,pszDIAERR_MUST_BE_UNIQUE2); + return NULL; + } + + //** Make sure file list exists + if (!psess->hglistFiles) { + psess->hglistFiles = GLCreateList(NULL, // No default + DestroyFileInfo, + pszDIA_FILE_INFO, + perr); + if (!psess->hglistFiles) { + return NULL; // perr already filled in + } + } + + //** Create file info + if (!(pfinfo = MemAlloc(sizeof(FILEINFO)))) { + ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_FILES); + return NULL; + } + if (!(pfinfo->pszDDF = MemStrDup(perr->pszFile))) { + ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_FILES); + MemFree(pfinfo); + return NULL; + } + + //** Add file to list + if (!(hgen = GLAdd(psess->hglistFiles, // List + pszDst, // key name + pfinfo, // file info + pszDIA_FILE, // Description for error message + fUnique, // Uniqueness setting + perr))) { + //** See if we need to remap error + if (perr->code == ERRGLIST_NOT_UNIQUE) { + //** Give info on where first file name was found + pfinfoFirst = GLGetValue(perr->pv); + AssertFinfo(pfinfoFirst); + ErrSet(perr,pszDIAERR_NOT_UNIQUE,"%s%s%d", + pszDst,pfinfoFirst->pszDDF,pfinfoFirst->ilineDDF); + } + MemFree(pfinfo->pszDDF); + MemFree(pfinfo); + return NULL; + } + + //** Initialize remainder of structure + pfinfo->ilineDDF = perr->iLine; // Set line number in DDF + pfinfo->cbFile = cbFile; // File size + pfinfo->iDisk = idiskBAD; // Not yet determined + pfinfo->iCabinet = icabBAD; // Not yet determined + pfinfo->iFile = (int)psess->cFiles;// File index in layout + pfinfo->fta.date = 0; // Not yet determined + pfinfo->fta.time = 0; // Not yet determined + pfinfo->fta.attr = 0; // Not yet determined + pfinfo->hglistInfLines = NULL; // No lines to print + pfinfo->flags = 0; // Reset all flags + pfinfo->hglistParm = pcmd->file.hglist; // Save file parameters + pfinfo->checksum = 0; // Not yet determined + pfinfo->verMS = 0; // Not yet determined + pfinfo->verLS = 0; // Not yet determined + pfinfo->pszVersion = NULL; // Not yet determined + pfinfo->pszLang = NULL; // Not yet determined + pcmd->file.hglist = NULL; // Nothing to free! + + //** Set signature after we get structure fully initialized + SetAssertSignature(pfinfo,sigFILEINFO); + return hgen; // +} /* addFileToSession() */ + + +/*** checkReferences - Make sure all files in layout section are referenced + * + * Entry: + * psess - Session + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; everything is dandy. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL checkReferences(PSESSION psess, PERROR perr) +{ + BOOL fInf; + BOOL fOK; + HGENERIC hgen; + int iLine; + PFILEINFO pfinfo; + PFILEPARM pfparm; + char *pszFile; + + AssertSess(psess); + + //** Only need to check if in relational mode + if (psess->ddfmode != ddfmodeRELATIONAL) { + return TRUE; + } + + //** Check list of files in layout to make sure all were referenced + fOK = TRUE; // Assume everything is OK + for (hgen = GLFirstItem(psess->hglistFiles); + hgen; + hgen = GLNextItem(hgen)) { + + pfinfo = GLGetValue(hgen); + AssertFinfo(pfinfo); + if (!(pfinfo->flags & fifREFERENCED)) { // Not referenced + fInf = TRUE; // Assume INF reference is required + pfparm = GLFindAndGetValue(pfinfo->hglistParm,pszFILE_INF); + if (pfparm) { // /Inf specfied + AssertFparm(pfparm); + if (-1 == (fInf = BOOLfromPSZ(pfparm->pszValue,perr))) { + return FALSE; // perr filled in already + } + } + if (fInf) { // Should have been referenced + ErrSet(perr,pszDIAERR_FILE_NOT_REFD,"%s",GLGetKey(hgen)); + + //** Save current position + pszFile = perr->pszFile; + iLine = perr->iLine; + + //** Set position of unreferenced file + perr->pszFile = pfinfo->pszDDF; + perr->iLine = pfinfo->ilineDDF; + + //** Print error + printError(psess,perr); // Show error + + //** Restore position + perr->pszFile = pszFile; + perr->iLine = iLine; + + //** Reset error so we can continue + ErrClear(perr); // Catch all errors + fOK = FALSE; // Remember we had an error + } + } + } + //** Return status + return fOK; +} /* checkReferences() */ + + +/** apszVarTemplate - List of variable name templates + * + * checkVariableDefinitions() uses this list to avoid complaining about + * "standard" variables that are of the form: Name<integer>. + */ +char *apszVarTemplate[] = { + pszPATTERN_VAR_CAB_NAME, + pszPATTERN_VAR_DISK_DIR, + pszPATTERN_VAR_DISK_LABEL, + pszPATTERN_VAR_INF_DISK_HEADER, + pszPATTERN_VAR_INF_DISK_LINE_FMT, + pszPATTERN_VAR_INF_CAB_HEADER, + pszPATTERN_VAR_INF_CAB_LINE_FMT, + pszPATTERN_VAR_INF_FILE_HEADER, + pszPATTERN_VAR_INF_FILE_LINE_FMT, + pszPATTERN_VAR_INF_HEADER, + pszPATTERN_VAR_INF_FOOTER, + pszPATTERN_VAR_MAX_DISK_SIZE, + NULL, +}; + + +/** apszParmNames - List of InfXxx suffixes for standard parameters + * + * checkVariableDefinitions() uses this list to avoid complaining about + * "standard" parameters that are of the form: Inf<parm>. + */ +char *apszParmNames[] = { + pszPARM_FILEATTR, + pszPARM_CAB_NUMBER, + pszPARM_CAB_FILE, + pszPARM_CHECKSUM, + pszPARM_FILEDATE, + pszPARM_DISK_NUMBER, + pszPARM_FILENAME, + pszPARM_FILE_NUMBER, + pszPARM_INF, + pszPARM_LABEL, + pszPARM_LANG, + pszPARM_FILESIZE, + pszPARM_FILETIME, + pszPARM_UNIQUE, + pszPARM_VERNUM, + pszPARM_VERSTR, +}; + + +/*** checkVariableDefinitions - Verify all variables are .Defined or PERM + * + * Entry: + * psess - Session + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; everything is dandy. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL checkVariableDefinitions(PSESSION psess, PERROR perr) +{ + int cb; + BOOL f; + BOOL fOK; + HVARIABLE hvar; + VARFLAGS vfl; + char **ppsz; + char *psz; + char *pszName; + char *pszSuffix; + + AssertSess(psess); + + //** Generate errors for any variables that are not permanent or were + // not .Defined. + // NOTE: We don't do this checking at .Set time, because user-defined + // variables may be specified on the command line, and we want + // to permit that *if* an INF file is specified. + // + fOK = TRUE; // Assume everything is OK + for (hvar = VarFirstVar(psess->hvlist); + hvar; + hvar = VarNextVar(hvar)) { + + vfl = VarGetFlags(hvar); + if (0 == (vfl & (vflPERM | vflDEFINE))) { //** Not defined + f = FALSE; // Assume variable is OK + pszName = VarGetName(hvar); + + //** See if this is one of the "template" variables + for (ppsz=apszVarTemplate; *ppsz; ppsz++) { + cb = strlen(*ppsz) - 1; + Assert(cb > 0); + Assert((*ppsz)[cb] == chDF_WILDCARD); + if (_strnicmp(pszName,*ppsz,cb) == 0) { + //** The prefix is valid, make sure rest is an integer + for (psz = pszName + cb; // Point at prefix + *psz && isdigit(*psz); + psz++) { + ; // Check that rest of string is digits + } + f = (*psz == '\0'); // OK if we ran off end of string + break; // No need to continue checking + } + } + + //** If no match so far, check for InfXxxx variable name + if (!f) { + //** See if this is one of the InfXxx parm default value vars + cb = strlen(pszPREFIX_INF_VARS); + if (_strnicmp(pszName,pszPREFIX_INF_VARS,cb) == 0) { + //** Starts with "Inf" -- check for parm name suffix + pszSuffix = pszName + cb; // Point at suffix + for (ppsz=apszParmNames; *ppsz; ppsz++) { + if (_stricmp(pszSuffix,*ppsz) == 0) { + f = TRUE; // Variable is OK + break; // Stop checking + } + } + } + } + + //** Check result of comparisons + if (!f) { + ErrSet(perr,pszDIAERR_UNDEFINED_VAR,"%s%s%s", + pszCMD_OPTION, + pszOPTION_EXPLICIT, + pszName); + + //** Print error + printError(psess,perr); // Show error + + //** Reset error so we can continue + ErrClear(perr); // Catch all errors + fOK = FALSE; // Remember we had an error + } + } + } + + //** Return status + return fOK; +} /* checkVariableDefinitions() */ + + +/*** getVarWithOverride - Checks for override for variable, gets value + * + * Entry: + * psess - Session + * pchDst - Buffer to receive constructed value + * cbDst - Size of pchDst + * pszPattern - Variable name pattern (i.e., CabinetLabel*) + * pszVar - Name of variable with default value (i.e., CabinetNameTemplate) + * i - Index to be used for pszPattern and pszTemplate + * pszKind - Description of variable (for error messages) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; buffer filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL getVarWithOverride(PSESSION psess, + char *pchDst, + int cbDst, + char *pszPattern, + char *pszVar, + int i, + char *pszKind, + PERROR perr) +{ + HVARIABLE hvar; + char *psz; + + AssertSess(psess); + + //** (1) Construct override variable name + if (!nameFromTemplate(psess->achMsg, + sizeof(psess->achMsg), + pszPattern, + i, + pszKind, + perr)) { + Assert(0); // Should never fail + return FALSE; // perr already filled in + } + + //** (2) See if override variable exists + hvar = VarFind(psess->hvlist,psess->achMsg,perr); + if (hvar != NULL) { // Yes, get its value + psz = VarGetString(hvar); // Get disk label + if (strlen(psz) >= (size_t)cbDst) { + ErrSet(perr,pszDIAERR_VALUE_TOO_LONG,"%s%d%s",pszKind,cbDst-1,psz); + return FALSE; + } + strcpy(pchDst,psz); + } + else { // NO, no override for this *i* + ErrClear(perr); // Not an error + //** Construct default value + hvar = VarFind(psess->hvlist,pszVar,perr); + Assert(!perr->fError); // Must be defined + psz = VarGetString(hvar); // Get template + if (!nameFromTemplate(pchDst, + cbDst, + psz, + i, + pszKind, + perr)) { + return FALSE; // perr already filled in + } + } + + //** Success + return TRUE; +} /* getVarWithOverride() */ + +//** Get CRC code +//BUGBUG 14-Dec-1994 bens Include code here to avoid makefile change +#include "crc32.c" + + +/*** getFileChecksum - Compute file checksum + * + * Entry: + * pszFile - Filespec + * pchecksum - Receives 32-bit checksum of file + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, *pchecksum filled in. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL getFileChecksum(char *pszFile, ULONG *pchecksum, PERROR perr) +{ +#define cbCSUM_BUFFER 4096 // File buffer size + int cb; // Amount of data in read buffer + ULONG csum=CRC32_INITIAL_VALUE; // Initialize CRC + char *pb=NULL; // Read buffer + int hf=-1; // File handle + int rc; + BOOL result=FALSE; // Assume failure + + //** Initialize returned checksum (assume failure) + *pchecksum = csum; + + //** Allocate file buffer + if (!(pb = MemAlloc(cbCSUM_BUFFER))) { + ErrSet(perr,pszDIAERR_NO_MEMORY_CRC,"%s",pszFile); + return FALSE; + } + + //** Open file + hf = _open(pszFile,_O_RDONLY | _O_BINARY,&rc); + if (hf == -1) { + ErrSet(perr,pszDIAERR_OPEN_FAILED,"%s",pszFile); + goto Exit; + } + + //** Compute checksum + while (_eof(hf) == 0) { + cb = _read(hf,pb,cbCSUM_BUFFER); + if (cb == -1) { + ErrSet(perr,pszDIAERR_READ_FAIL_CRC,"%s",pszFile); + goto Exit; + } + if (cb != 0) { + csum = CRC32Compute(pb,cb,csum); // Accumulate CRC + } + } + + //** Success + result = TRUE; + *pchecksum = csum; // Store checksum for caller + +Exit: + if (hf != -1) { + _close(hf); + } + if (pb != NULL) { + MemFree(pb); + } + return result; +} /* getFileChecksum() */ diff --git a/private/windows/diamond/diamond.msg b/private/windows/diamond/diamond.msg new file mode 100644 index 000000000..3a7b68402 --- /dev/null +++ b/private/windows/diamond/diamond.msg @@ -0,0 +1,231 @@ +/*** diamond.msg - DIAMOND.EXE displayable strings + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Added more directives and variables + * 12-Aug-1993 bens Moved strings to individual .MSG files + * 20-Aug-1993 bens Added more variables and commands + * 21-Aug-1993 bens Added command line error messages + * 10-Feb-1994 bens Added more error messages, inc'd version + * 14-Feb-1994 bens Error messages for FCI errors + * 15-Feb-1994 bens Error messages for compressing a single file + * 16-Feb-1994 bens Error message for not a file + * 17-Feb-1994 bens Error messages + * 21-Feb-1994 bens Split off fileutil.*, better stdout formatting + * 24-Feb-1994 bens Generate that nice INF file + * 01-Mar-1994 bens Nicer INF file, RPT file added + * 17-Mar-1994 bens RESERVE support added + * 22-Mar-1994 bens Refresh with more robust FCI + * 30-Mar-1994 bens Implement .New command + * 01-Apr-1994 bens Fix disk overflow accounting bug + * 18-Apr-1994 bens Pick up bug fix for incompressible data + * 18-Apr-1994 bens Add /L switch + * 04-May-1994 bens Add customizable INF stuff + * 18-May-1994 bens More customizable INF + * 27-May-1994 bens Ver, Lang, Quantum support + * 12-Aug-1994 bens Fix disk space accounting when .New Disk occurs + * when Cabinet=ON. + * 14-Dec-1994 bens *csum* implemented. + * 28-Mar-1995 jeffwe Add ChecksumWidth + */ + +//** Command Line Switches + +#ifdef BIT16 +#define pszDIAMOND_VERSION "(16) 1.00.0540 (02/01/96)" // For %1 in pszBANNER +#else +#define pszDIAMOND_VERSION "(32) 1.00.0540 (02/01/96)" // For %1 in pszBANNER +#endif + +#define pszBANNER \ + "Microsoft (R) Diamond Disk Layout Tool - Version %1\n" \ + "Copyright (c) Microsoft Corp 1993-1996. All rights reserved.\n" + +#define pszCMD_LINE_HELP \ + "DIAMOND [/V[n]] [/D var=value ...] [/L dir] source [destination]\n" \ + "DIAMOND [/V[n]] [/D var=value ...] /F directive_file [...]\n" \ + "\n" \ + " source File to compress.\n" \ + " destination File name to give compressed file. If omitted, the\n" \ + " last character of the source file name is replaced\n" \ + " with an underscore (_) and used as the destination.\n" \ + " /F directives A file with Diamond directives (may be repeated).\n" \ + " /D var=value Defines variable with specified value.\n" \ + " /L dir Location to place destination (default is current directory).\n" \ + " /V[n] Verbosity level (1..3).\n" + +#ifdef ASSERT +#define pszCMD_LINE_HELP_DBG \ + " /M ASSERT: Do FULL memory heap checking (slow!).\n" +#endif + + +#define chSWITCH1 '/' +#define chSWITCH2 '-' + +#define chSWITCH_HELP '?' + +#ifndef REMOVE_CHICAGO_M6_HACK +#define chSWITCH_ANDY 'A' +#endif + +#define chSWITCH_DEFINE 'D' +#define chSWITCH_FILE 'F' +#define chSWITCH_LOCATION 'L' +#ifdef ASSERT +#define chSWITCH_MEMORY_DBG 'M' +#endif +#define chSWITCH_VERBOSE 'V' + + +//** Status messages + +#define pszDIA_PASS_1_HEADER1 "PASS 1: Checking directive file(s)" +#define pszDIA_PASS_1_HEADER2 "----------------------------------" +#define pszDIA_PASS_2_HEADER1 "PASS 2: Processing directive file(s)" +#define pszDIA_PASS_2_HEADER2 "------------------------------------" + +#define pszDIA_PARSING_DIRECTIVES "Parsing directives" +#define pszDIA_PARSING_PROGRESS "Parsing directives (%1: %2 lines)" +#define pszDIA_EXECUTING_DIRECTIVES "Executing directives" + +#define pszDIA_PERCENT_COMPLETE_SOME "%1%% - %2 (%3 of %4)" + +#define pszDIA_STATS_BEFORE "%1 bytes in %2 files" + +#define pszDIA_RPT_HEADER "Diamond Report: %1" + +#define pszDIA_STATS_AFTER1 "Total files: %1" +#define pszDIA_STATS_AFTER2 "Bytes before: %1" +#define pszDIA_STATS_AFTER3 "Bytes after: %1" +#define pszDIA_STATS_AFTER4 "After/Before: %1%% compression" +#define pszDIA_STATS_AFTER5 "Time: %1 seconds (%2 hr %3 min %4 sec)" +#define pszDIA_STATS_AFTER6 "Throughput: %1 Kb/second" + +#define pszDIA_FILE_COPY "CopyCommand: %1 to %2" +#define pszDIA_FILE_IN_CAB "** %1 placed in cabinet %2(%3) on disk %4" +#define pszDIA_FILE_IN_CAB_CONT "** %1 placed in cabinet %2(%3) on disk %4 - CONTINUATION" + +#define pszDIA_PERCENT_COMPLETE_DETAILS "%1%% - raw=%2 compressed=%3" + +#define pszDIA_VAR_DUMP1 "Diamond Variables (in order of their definition)" +#define pszDIA_VAR_DUMP2 "------------------------------------------------" +#define pszDIA_VAR_PERMANENT "Standard" +#define pszDIA_VAR_DEFINED "Defined" + + +//** Component strings (used in error messages) + +#define pszDIA_CABINET "cabinet file name" +#define pszDIA_DISK_DIR "disk directory name" +#define pszDIA_DISK_LABEL "disk label name" + +#define pszDIA_FILE "file" +#define pszDIA_FILE_INFO "file info" +#define pszDIA_LINE_INFO "INF file line" + +#define pszDIA_INF_FILE "INF file" +#define pszDIA_RPT_FILE "RPT file" + +#define pszDIA_FILE_PARM "file parameter" + +//** Error messages + +#define pszDIAERR_ERROR "ERROR" +#define pszDIAERR_BAD_SWITCH "Invalid switch: %1" +#define pszDIAERR_BAD_PARAMETER "Invalid parameter: %1" +#define pszDIAERR_SWITCH_NOT_EXPECTED "Switch not expected: %1" +#define pszDIAERR_TWO_MANY_PARMS "Too many parameters: %1" +#define pszDIAERR_MISSING_VAR_DEFINE "Variable defintion missing" +#define pszDIAERR_MISSING_FILE_NAME "Directive file name missing" +#define pszDIAERR_MISSING_LOCATION "Location missing" +#define pszDIAERR_LOCATION_TOO_LONG "Location too long: %1" +#define pszDIAERR_ERRORS_IN_PASS_1 "Diamond aborted: %1 errors encountered" +#define pszDIAERR_MAX_ERRORS "Diamond aborted: MaxErrors (%1) exceeded" +#define pszDIAERR_ERRORS_IN_PASS_2 "Diamond aborted." + +#define pszDIAERR_PATH_TOO_LONG "File name too long: %1" +#define pszDIAERR_NO_SESSION "Could not allocate SESSION" +#define pszDIAERR_MULTIPLE_CABINETS "INTERR: GetNextCab called for %1" +#define pszDIAERR_VALUE_TOO_LONG "%1 exceeds maximum length (%2): %3" + // These values are substituted for %1 in the above message. + // pszDIA_CABINET + // pszDIA_DISK_DIR + // pszDIA_DISK_LABEL + +#define pszDIAERR_CANT_CREATE_RPT "Cannot create report file: %1" +#define pszDIAERR_OPEN_FAILED "Could not open file: %1" +#define pszDIAERR_SINGLE_COMPRESS "NOT IMPLEMENTED YET: compressing single files: %1" +#define pszDIAERR_BAD_NEW_CMD "Directive '%1 %2' not valid outside of a cabinet" +#define pszDIAERR_BAD_CMD_IN_INF_SECT "Directive '%1' not valid in INF section" +#define pszDIAERR_DISK_CLUSTER_SIZE "MaxDiskSize(%1) is not a multiple of ClusterSize(%2)" + +#define pszDIAERR_OUT_OF_MEMORY "Out of memory %1" +#define pszDIAOOM_TRACKING_FILES "tracking files" +#define pszDIAOOM_TRACKING_LINES "tracking INF file lines" + +#define pszDIA_BAD_INF_MODE "Cannot change INF generation setting" +#define pszDIAERR_MUST_BE_UNIQUE "UniqueFiles must be ON for relational INF generation" +#define pszDIAERR_MUST_BE_UNIQUE2 "/unique=OFF not allowed for relational INF generation" + +#define pszDIAERR_INF_IN_LAYOUT "Illegal to write to INF file area in layout section" +#define pszDIAERR_REF_FILE_NOT_FOUND "No such file in layout section: %1" +#define pszDIAERR_FILE_NOT_REFD "File in layout section not referenced in INF section: %1" + +#define pszDIAERR_NOT_UNIQUE "Duplicate file name: %1 already defined at %2(%3)" + +#define pszDIAERR_REDEFINE "Cannot %1 a variable twice : %2" +#define pszDIAERR_DEFINE_PERM "Cannot %1 a standard variable: %2" +#define pszDIAERR_UNDEFINED_VAR "%1 %2 and variable not defined: %3" + +#define pszDIAERR_NO_MEMORY_CRC "Out of memory doing CRC on file: %1" +#define pszDIAERR_READ_FAIL_CRC "Read failure doing CRC on file: %1" + + +//** FCI error messages +#define szFCI_CREATE "FCICreate" +#define szFCI_ADD_FILE "FCIAddFile" +#define szFCI_FLUSH_FOLDER "FCIFlushFolder" +#define szFCI_FLUSH_CABINET "FCIFlushCabinet" +#define szFCI_DESTROY "FCIDestroy" + + +#define pszFCIERR_ALLOC_FAIL "(%1)Out of memory" +#define pszFCIERR_BAD_COMPR_TYPE "(%1)Unknown compression type" +#define pszFCIERR_MCI_FAIL "(%1)Failure compressing data from file %2" +#define pszFCIERR_USER_ABORT "(%1)User aborted" +#define pszFCIERR_OPEN_SRC "(%1)Failure opening source file %2: %3" +#define pszFCIERR_READ_SRC "(%1)Failure reading source file %2: %3" +#define pszFCIERR_TEMP_FILE "(%1)Failure on temporary file: %2" +#define pszFCIERR_CAB_FILE "(%1)Failure creating or writing cabinet file: %2" + +#ifndef REMOVE_CHICAGO_M6_HACK +#define pszFCIERR_M6_HACK_INCOMPRESSIBLE "(%1)Incompressible data (/A specified): %2" +#endif + +#define pszFCIERR_UNKNOWN_ERROR "(%1)Unknown error(%2)" + + +//** C Run-Time Library error messages + +#define pszCRTERRNO_ECHILD "no child processes" +#define pszCRTERRNO_EAGAIN "create process failed" +#define pszCRTERRNO_E2BIG "arg list too long/out of environment space" +#define pszCRTERRNO_EACCES "permission denied" +#define pszCRTERRNO_EBADF "bad file handle/incompatible I/O operation" +#define pszCRTERRNO_EDEADLOCK "deadlock would occur?" +#define pszCRTERRNO_EDOM "bad argument to math function" +#define pszCRTERRNO_EEXIST "file already exists" +#define pszCRTERRNO_EINVAL "invalid argument" +#define pszCRTERRNO_EMFILE "out of file handles" +#define pszCRTERRNO_ENOENT "file/path does not exist" +#define pszCRTERRNO_ENOEXEC "invalid executable format" +#define pszCRTERRNO_ENOMEM "out of memory" +#define pszCRTERRNO_ENOSPC "out of disk space" +#define pszCRTERRNO_ERANGE "math argument out of range" +#define pszCRTERRNO_EXDEV "cannot move file across devices" +#define pszCRTERRNO_UNKNOWN "<unknown C run-time error>" diff --git a/private/windows/diamond/dirs b/private/windows/diamond/dirs new file mode 100644 index 000000000..d55c35d74 --- /dev/null +++ b/private/windows/diamond/dirs @@ -0,0 +1 @@ +DIRS=mszip quantum qstub chuck tools diff --git a/private/windows/diamond/dmfon.h b/private/windows/diamond/dmfon.h new file mode 100644 index 000000000..f310dd6c2 --- /dev/null +++ b/private/windows/diamond/dmfon.h @@ -0,0 +1,26 @@ +/*** dmfon.h - DMF (Distribution Media Format -- 1.7M 3.5") support + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 13-May-1994 bens Initial version + */ + + +/*** EnableDMFSupport - Enable DMF support on pre-Chicago DOS systems + * + * Entry: + * None: + * + * Exit: + * INT 13h vector hooked with code to ensure DOS and BIOS read + * DMF disks correctly. + * PSP:savedINT22 hooked so that we can *unhook* our INT13 hook + * when the calling program exits. + */ +void far EnableDMFSupport(void); diff --git a/private/windows/diamond/dmftools/dmffmt/dmffmt.c b/private/windows/diamond/dmftools/dmffmt/dmffmt.c new file mode 100644 index 000000000..11a4a8d5f --- /dev/null +++ b/private/windows/diamond/dmftools/dmffmt/dmffmt.c @@ -0,0 +1,199 @@ +#include <windows.h> +#include <winioctl.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +typedef struct _BOOT_SECTOR_ZERO { + UCHAR IntelNearJumpCommand[1]; + UCHAR BootStrapJumpOffset[2]; + UCHAR OemData[8]; + UCHAR BytesPerSector[2]; + UCHAR SectorsPerCluster[1]; + UCHAR ReservedSectors[2]; + UCHAR Fats[1]; + UCHAR RootEntries[2]; + UCHAR Sectors[2]; + UCHAR Media[1]; + UCHAR SectorsPerFat[2]; + UCHAR SectorsPerTrack[2]; + UCHAR Heads[2]; + UCHAR HiddenSectors[4]; + UCHAR LargeSectors[4]; + UCHAR PhysicalDrive[1]; + UCHAR CurrentHead[1]; + UCHAR Signature[1]; + UCHAR SerialNumber[4]; + UCHAR Label[11]; + UCHAR SystemIdText[8]; +} *PBOOT_SECTOR; + +int +_cdecl +main( + int argc, + char** argv + ) + +/*++ + +Routine Description: + + This routine implements a DMF format. It lays down the BPB and everything. + +Arguments: + + argc - Supplies the number of command line arguments. + + argv - Supplies the command line arguments. + +Return Value: + + 0 - Success. + + 1 - Failure. + +--*/ + +{ +#define FORMAT_PARAMETERS_SIZE (sizeof(FORMAT_EX_PARAMETERS) + 20*sizeof(USHORT)) + + CHAR drive[20]; + HANDLE handle; + CHAR formatParametersBuffer[FORMAT_PARAMETERS_SIZE]; + PFORMAT_EX_PARAMETERS formatParameters; + ULONG i, next; + BOOL b; + PUCHAR trackBuffer; + ULONG trackSize; + PBOOT_SECTOR bootSector; + DWORD bytesWritten; + USHORT swapBuffer[3]; + + // First make sure that we have at least one parameter. + + if (argc < 2) { + printf("usage: %s drive:\n", argv[0]); + return(1); + } + + + // Open the drive for exclusive access. + + sprintf(drive, "\\\\.\\%s", argv[1]); + handle = CreateFile(drive, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + printf("can't open drive %s, error = %d\n", argv[1], GetLastError()); + return(1); + } + + + // Prepare the format parameters. + + formatParameters = (PFORMAT_EX_PARAMETERS) formatParametersBuffer; + formatParameters->MediaType = F3_1Pt44_512; + formatParameters->FormatGapLength = 8; + formatParameters->SectorsPerTrack = 21; + + next = 0; + for (i = 0; i < formatParameters->SectorsPerTrack; i += 2) { + formatParameters->SectorNumber[i] = (USHORT) (i/2 + 1); + next++; + } + + for (i = 1; i < formatParameters->SectorsPerTrack; i += 2) { + formatParameters->SectorNumber[i] = (USHORT) (i/2 + next + 1); + } + + // Start off by putting the boot sector as the last sector of + // the first track. + + MoveMemory(&formatParameters->SectorNumber[0], + &formatParameters->SectorNumber[1], + 20*sizeof(USHORT)); + formatParameters->SectorNumber[20] = 1; + + + // Format each track on the floppy. + + for (i = 0; i < 80; i++) { + + formatParameters->StartCylinderNumber = + formatParameters->EndCylinderNumber = i; + formatParameters->StartHeadNumber = 0; + formatParameters->EndHeadNumber = 1; + + printf("Formatting track %d\r", i); + + b = DeviceIoControl(handle, IOCTL_DISK_FORMAT_TRACKS_EX, + formatParameters, FORMAT_PARAMETERS_SIZE, + NULL, 0, &bytesWritten, NULL); + + if (!b) { + printf("Could not format track %d, error = %d\n", i, GetLastError()); + return(1); + } + + + // Skew the next cylinder by 3 sectors from this one. + + MoveMemory(swapBuffer, + &formatParameters->SectorNumber[18], + 3*sizeof(USHORT)); + MoveMemory(&formatParameters->SectorNumber[3], + &formatParameters->SectorNumber[0], + 18*sizeof(USHORT)); + MoveMemory(&formatParameters->SectorNumber[0], + swapBuffer, + 3*sizeof(USHORT)); + + } + + + // Write out the boot sector, the FAT, and the root directory. + + trackSize = formatParameters->SectorsPerTrack*512; + trackBuffer = LocalAlloc(LMEM_FIXED, trackSize + 0x1FF); + trackBuffer = (PUCHAR) (((ULONG) (trackBuffer + 0x1FF))&(~0x1FF)); + ZeroMemory(trackBuffer, trackSize); + bootSector = (PBOOT_SECTOR) trackBuffer; + + bootSector->IntelNearJumpCommand[0] = 0xeb; + bootSector->BootStrapJumpOffset[0] = 0x3e; + bootSector->BootStrapJumpOffset[1] = 0x90; + CopyMemory(bootSector->OemData, "NSDMF3.2", 8); + bootSector->BytesPerSector[1] = 2; + bootSector->SectorsPerCluster[0] = 4; + bootSector->ReservedSectors[0] = 1; + bootSector->Fats[0] = 2; + bootSector->RootEntries[0] = 0x10; + bootSector->Sectors[0] = 0x20; + bootSector->Sectors[1] = 0xd; + bootSector->Media[0] = 0xf0; + bootSector->SectorsPerFat[0] = 3; + bootSector->SectorsPerTrack[0] = 0x15; + bootSector->Heads[0] = 2; + bootSector->Signature[0] = 0x29; + CopyMemory(bootSector->Label, "NO NAME ", 11); + CopyMemory(bootSector->SystemIdText, "FAT12 ", 8); + + trackBuffer[0x1fe] = 0x55; + trackBuffer[0x1ff] = 0xAA; + trackBuffer[0x200] = 0xF0; + trackBuffer[0x201] = 0xFF; + trackBuffer[0x202] = 0xFF; + trackBuffer[0x800] = 0xF0; + trackBuffer[0x801] = 0xFF; + trackBuffer[0x802] = 0xFF; + + b = WriteFile(handle, trackBuffer, trackSize, &bytesWritten, NULL); + if (!b || trackSize != bytesWritten) { + printf("Track 0 write failed with %d\n", GetLastError()); + return(1); + } + + CloseHandle(handle); + + return(0); +} diff --git a/private/windows/diamond/dmftools/dmffmt/makefile b/private/windows/diamond/dmftools/dmffmt/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/windows/diamond/dmftools/dmffmt/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/windows/diamond/dmftools/dmffmt/sources b/private/windows/diamond/dmftools/dmffmt/sources new file mode 100644 index 000000000..fe616bb8e --- /dev/null +++ b/private/windows/diamond/dmftools/dmffmt/sources @@ -0,0 +1,31 @@ +!IF 0 + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + +!ENDIF + +USE_CRTDLL=1 +MAJORCOMP= +MINORCOMP= + +TARGETNAME=dmffmt +TARGETPATH=obj +TARGETTYPE=PROGRAM + +SOURCES=dmffmt.c + +UMTYPE=console +UMAPPL=dmffmt +UMLIBS= + diff --git a/private/windows/diamond/dmftools/dmfimage/dmfimage.c b/private/windows/diamond/dmftools/dmfimage/dmfimage.c new file mode 100644 index 000000000..25849d6fa --- /dev/null +++ b/private/windows/diamond/dmftools/dmfimage/dmfimage.c @@ -0,0 +1,490 @@ + +#include "precomp.h" +#pragma hdrstop + +///////////////////////////////////////////////////////////////////////////// +// // +// dmfimage.c Program for retrieving entire contents of a floppy disk // +// into an image file, or writing such an image file to // +// a floppy disk. // +// // +// Code is targeted as Win32 console application. // +// // +// Author: Tom McGuire (tommcg) // +// // +// Original version written May, 1994. // +// // +// (C) Copyright 1994-1996, Microsoft Corporation // +// // +///////////////////////////////////////////////////////////////////////////// + +#define DEVICE_TO_FILE 0 +#define FILE_TO_DEVICE 1 + +#define FILE_NO_SHARING 0 + +#define GETLASTERROR 0xFFFFFFFF + +#define IMAGE_SIZE_DMF 1720320 // 80x2x21x512 (DMF 1.7MB) +#define IMAGE_SIZE_144 1474560 // 80x2x18x512 (standard 1.44MB) +#define IMAGE_SIZE_12 1228800 // 80x2x15x512 (standard 1.2MB) + +void FormatDmf( HANDLE hDevice ); +void FormatHD( HANDLE hDevice, MEDIA_TYPE eMediaType ); +void ErrorExit( DWORD dwGLE, const char *szFormat, ... ); +void Copyright( void ); + + +void __cdecl main( int argc, char *argv[] ) { + + CHAR chDeviceName[ MAX_PATH ] = "\\\\.\\"; + CHAR chFileName[ MAX_PATH ]; + DWORD dwFileSize = 0; + DWORD dwMediaSize, dwActual, dwGLE; + HANDLE hFile, hDevice, hMap; + PVOID pMappedView; + DISK_GEOMETRY dg; + DISK_GEOMETRY dgArray[ 20 ]; + INT fDirection = 0; + BOOL bFormatTarget = FALSE; + BOOL b288 = FALSE; + UINT n, nElements; + + + Copyright(); + + // + // Check usage. + // + + if ( argc < 3 ) { + ErrorExit( 0, "\n" + "Usage: DMFIMAGE source target [-f]\n" + "\n" + " Either source or target must be a floppy drive.\n" + " -f indicates format target media before writing.\n" + "\n" + "Examples: DMFIMAGE a: myfile.img\n" + " DMFIMAGE myfile.img b: -f\n" + "\n" ); + } + + + SetErrorMode( SEM_FAILCRITICALERRORS ); // no drive-not-ready popups + SetFileApisToOEM(); + + + // + // Determine source and target. + // + + if (( argv[ 1 ][ 1 ] == ':' ) && ( argv[ 1 ][ 2 ] == '\0' )) { + strcat( chDeviceName, argv[ 1 ] ); + strcpy( chFileName, argv[ 2 ] ); + fDirection = DEVICE_TO_FILE; + if ( ( argv[ 2 ][ 1 ] == ':' ) && ( argv[ 2 ][ 2 ] == '\0' )) { + ErrorExit( 0, "Either source or destination must be a file\n" ); + } + } + else if ( ( argv[ 2 ][ 1 ] == ':' ) && ( argv[ 2 ][ 2 ] == '\0' )) { + strcat( chDeviceName, argv[ 2 ] ); + strcpy( chFileName, argv[ 1 ] ); + fDirection = FILE_TO_DEVICE; + } + else { + ErrorExit( 0, "Either source or destination must be floppy drive " + "(like A:)\n" ); + } + + if (( argc > 3 ) && + (( argv[ 3 ][ 0 ] == '/' ) || ( argv[ 3 ][ 0 ] == '-' )) && + (( argv[ 3 ][ 1 ] == 'f' ) || ( argv[ 3 ][ 1 ] == 'F' ))) { + + bFormatTarget = TRUE; + } + + // + // Open device and determine size. + // + + hDevice = CreateFile( chDeviceName, + ( fDirection == FILE_TO_DEVICE ) ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ, + ( fDirection == FILE_TO_DEVICE ) ? FILE_NO_SHARING : FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING, + NULL ); + + if ( hDevice == INVALID_HANDLE_VALUE ) { + ErrorExit( GETLASTERROR, "Could not open device %s\n", + chDeviceName ); + } + + if ( ! DeviceIoControl( hDevice, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, + 0, + &dg, + sizeof( dg ), + &dwActual, + NULL )) { + ErrorExit( GETLASTERROR, "Unable to query disk information on %s\n", + chDeviceName ); + } + + if ( dg.MediaType == Unknown ) { + dwMediaSize = 0; + bFormatTarget = TRUE; + } + else { + dwMediaSize = dg.Cylinders.LowPart + * dg.TracksPerCylinder + * dg.SectorsPerTrack + * dg.BytesPerSector; + } + + if ( ! DeviceIoControl( hDevice, + IOCTL_DISK_GET_MEDIA_TYPES, + NULL, + 0, + &dgArray, + sizeof( dgArray ), + &dwActual, + NULL )) { + ErrorExit( GETLASTERROR, "Unable to query disk information on %s\n", + chDeviceName ); + } + + nElements = dwActual / sizeof( dgArray[ 0 ] ); + + for ( n = 0; n < nElements; n++ ) { + switch ( dgArray[ n ].MediaType ) { + case F5_1Pt2_512: + if ( dwMediaSize == 0 ) + dwMediaSize = IMAGE_SIZE_12; + break; + case F3_1Pt44_512: + if ( dwMediaSize == 0 ) + dwMediaSize = IMAGE_SIZE_144; + break; + case F3_2Pt88_512: + b288 = TRUE; + break; + } + } + + // + // Open file + // + + hFile = CreateFile( chFileName, + ( fDirection == FILE_TO_DEVICE ) ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + ( fDirection == FILE_TO_DEVICE ) ? FILE_SHARE_READ : FILE_NO_SHARING, + NULL, + ( fDirection == FILE_TO_DEVICE ) ? OPEN_EXISTING : CREATE_ALWAYS, + ( fDirection == FILE_TO_DEVICE ) ? FILE_FLAG_SEQUENTIAL_SCAN : FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_NO_BUFFERING, + NULL ); + + if ( hFile == INVALID_HANDLE_VALUE ) { + ErrorExit( GETLASTERROR, "Could not open file %s\n", + chFileName ); + } + + // + // If reading from file, make sure file is correct size for media + // + + if ( fDirection == FILE_TO_DEVICE ) { + + dwFileSize = GetFileSize( hFile, NULL ); + + if (( dwFileSize == IMAGE_SIZE_DMF ) && + (( dwMediaSize == IMAGE_SIZE_DMF ) || + ( dwMediaSize == IMAGE_SIZE_144 ))) { + + if ( b288 ) { + ErrorExit( 0, "Cannot create DMF diskette in 2.88MB drive\n" ); + } + + // + // DMF targets always get formatted + // + + bFormatTarget = TRUE; + dwMediaSize = IMAGE_SIZE_DMF; + } + + else if (( dwFileSize == IMAGE_SIZE_144 ) && + ( dwMediaSize == IMAGE_SIZE_DMF )) { + + bFormatTarget = TRUE; + dwMediaSize = IMAGE_SIZE_144; + } + + + if ( bFormatTarget ) { + + if (( dwMediaSize != IMAGE_SIZE_DMF ) && + ( dwMediaSize != IMAGE_SIZE_144 ) && + ( dwMediaSize != IMAGE_SIZE_12 )) { + + ErrorExit( 0, "Only 1.2MB, 1.44MB, and DMF formatting is supported\n" ); + } + } + + if ( dwFileSize != dwMediaSize ) { + ErrorExit( 0, "Source image is not compatible with target media\n" + "(source size is %d bytes but target media is %d)\n", + dwFileSize, + dwMediaSize ); + } + + if ( bFormatTarget ) { + + printf( "Formatting..." ); + + switch ( dwMediaSize ) { + case IMAGE_SIZE_DMF: + FormatDmf( hDevice ); + break; + case IMAGE_SIZE_144: + FormatHD( hDevice, F3_1Pt44_512 ); + break; + case IMAGE_SIZE_12: + FormatHD( hDevice, F5_1Pt2_512 ); + } + + printf( "done\n" ); + + } + } + + // + // Map file for transfer + // + + hMap = CreateFileMapping( hFile, + NULL, + ( fDirection == FILE_TO_DEVICE ) ? PAGE_READONLY : PAGE_READWRITE, + 0, + ( fDirection == FILE_TO_DEVICE ) ? dwFileSize : dwMediaSize, + NULL ); + + if ( hMap == NULL ) { + dwGLE = GetLastError(); + if ( fDirection == DEVICE_TO_FILE ) { + CloseHandle( hFile ); + DeleteFile( chFileName ); + } + ErrorExit( dwGLE, "Unable to map file %s\n", chFileName ); + } + + pMappedView = MapViewOfFile( hMap, + ( fDirection == FILE_TO_DEVICE ) ? FILE_MAP_READ : FILE_MAP_WRITE, + 0, + 0, + 0 ); + + if ( pMappedView == NULL ) { + dwGLE = GetLastError(); + if ( fDirection == DEVICE_TO_FILE ) { + CloseHandle( hMap ); + CloseHandle( hFile ); + DeleteFile( chFileName ); + } + ErrorExit( dwGLE, "Unable to map file %s\n", chFileName ); + } + + + // + // Now we're ready to transfer. + // + + printf( "Copying..." ); + + try { // might take in-page exception from mapped view error + + if ( fDirection == FILE_TO_DEVICE ) { + + if ( ! WriteFile( hDevice, + pMappedView, + dwFileSize, + &dwActual, + NULL )) { + + ErrorExit( GETLASTERROR, "error while copying\n" ); + } + } + + else { // ( fDirection == DEVICE_TO_FILE ) + + if ( ! ReadFile( hDevice, + pMappedView, + dwMediaSize, + &dwActual, + NULL )) { + + ErrorExit( GETLASTERROR, "error while copying\n" ); + } + } + } + + except( EXCEPTION_EXECUTE_HANDLER ) { + ErrorExit( 0, "exception %08X while copying\n", GetExceptionCode() ); + } + + printf( "done\n" ); + + // + // cleanup is free + // + + exit( 0 ); + + } + + +void FormatDmf( HANDLE hDevice ) { + + #define FORMAT_PARAMETERS_SIZE ( sizeof( FORMAT_EX_PARAMETERS ) \ + + sizeof( USHORT ) * 20 ) + + CHAR formatParametersBuffer[FORMAT_PARAMETERS_SIZE]; + PFORMAT_EX_PARAMETERS formatParameters; + ULONG i, next; + BOOL b; + DWORD bytesWritten; + USHORT swapBuffer[3]; + + // Prepare the format parameters. + + formatParameters = (PFORMAT_EX_PARAMETERS) formatParametersBuffer; + formatParameters->MediaType = F3_1Pt44_512; + formatParameters->FormatGapLength = 8; + formatParameters->SectorsPerTrack = 21; + + next = 0; + for (i = 0; i < formatParameters->SectorsPerTrack; i += 2) { + formatParameters->SectorNumber[i] = (USHORT) (i/2 + 1); + next++; + } + + for (i = 1; i < formatParameters->SectorsPerTrack; i += 2) { + formatParameters->SectorNumber[i] = (USHORT) (i/2 + next + 1); + } + + // Start off by putting the boot sector as the last sector of + // the first track. + + MoveMemory(&formatParameters->SectorNumber[0], + &formatParameters->SectorNumber[1], + 20*sizeof(USHORT)); + formatParameters->SectorNumber[20] = 1; + + + // Format each track on the floppy. + + for (i = 0; i < 80; i++) { + + formatParameters->StartCylinderNumber = + formatParameters->EndCylinderNumber = i; + formatParameters->StartHeadNumber = 0; + formatParameters->EndHeadNumber = 1; + + b = DeviceIoControl(hDevice, IOCTL_DISK_FORMAT_TRACKS_EX, + formatParameters, FORMAT_PARAMETERS_SIZE, + NULL, 0, &bytesWritten, NULL); + + if ( ! b ) { + ErrorExit( GETLASTERROR, "DMF format failed\n" ); + } + + // Skew the next cylinder by 3 sectors from this one. + + MoveMemory(swapBuffer, + &formatParameters->SectorNumber[18], + 3*sizeof(USHORT)); + MoveMemory(&formatParameters->SectorNumber[3], + &formatParameters->SectorNumber[0], + 18*sizeof(USHORT)); + MoveMemory(&formatParameters->SectorNumber[0], + swapBuffer, + 3*sizeof(USHORT)); + + } + } + + +void FormatHD( HANDLE hDevice, MEDIA_TYPE eMediaType ) { + + FORMAT_PARAMETERS fp; + DWORD dwActual; + BOOL bSuccess; + + if (( eMediaType != F5_1Pt2_512 ) && + ( eMediaType != F3_1Pt44_512 )) { + ErrorExit( 0, "Invalid media type for format request\n" ); + } + + fp.MediaType = eMediaType; + fp.StartCylinderNumber = 0; + fp.EndCylinderNumber = 79; + fp.StartHeadNumber = 0; + fp.EndHeadNumber = 1; + + bSuccess = DeviceIoControl( hDevice, + IOCTL_DISK_FORMAT_TRACKS, + &fp, + sizeof( fp ), + NULL, + 0, + &dwActual, + NULL ); + + if ( ! bSuccess ) { + ErrorExit( GETLASTERROR, "format failed\n" ); + } + } + +void ErrorExit( DWORD dwGLE, const char *szFormat, ... ) { + + CHAR chBuffer[ 256 ]; + va_list vaArgs; + + if ( dwGLE == GETLASTERROR ) + dwGLE = GetLastError(); + + va_start( vaArgs, szFormat ); + vfprintf( stdout, szFormat, vaArgs ); + va_end( vaArgs ); + + if ( dwGLE ) { + if ( FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwGLE, + 0x409, + chBuffer, + sizeof( chBuffer ), + NULL )) { + printf( "(%d) %s\n", dwGLE, chBuffer ); + } + else { + printf( "Error code %d\n", dwGLE ); + } + } + + exit( 1 ); + + } + + +void Copyright( void ) { + + printf( "\n" + "DMFIMAGE Diskette Image Utility Version 1.05\n" + "Copyright (C) Microsoft, 1994-1996. All rights reserved.\n" + "For Microsoft internal use only.\n" + "\n" ); + + } diff --git a/private/windows/diamond/dmftools/dmfimage/makefile b/private/windows/diamond/dmftools/dmfimage/makefile new file mode 100644 index 000000000..5acbbd24c --- /dev/null +++ b/private/windows/diamond/dmftools/dmfimage/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/windows/diamond/dmftools/dmfimage/precomp.h b/private/windows/diamond/dmftools/dmfimage/precomp.h new file mode 100644 index 000000000..2a014e1d1 --- /dev/null +++ b/private/windows/diamond/dmftools/dmfimage/precomp.h @@ -0,0 +1,7 @@ +#pragma warning( disable: 4001 4201 4214 4514 ) +#include <windows.h> +#pragma warning( disable: 4001 4201 4214 4514 ) +#include <winioctl.h> +#include <stdlib.h> +#include <stdio.h> + diff --git a/private/windows/diamond/dmftools/dmfimage/sources b/private/windows/diamond/dmftools/dmfimage/sources new file mode 100644 index 000000000..cc187641f --- /dev/null +++ b/private/windows/diamond/dmftools/dmfimage/sources @@ -0,0 +1,17 @@ +MAJORCOMP=setup +MINORCOMP=dmfimage + +TARGETNAME=dmfimage +TARGETPATH=obj +TARGETTYPE=UMAPPL_NOLIB + +SOURCES= + +UMTYPE=console +UMAPPL=dmfimage + +USE_CRTDLL=1 + +PRECOMPILED_INCLUDE=precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/windows/diamond/dmftools/dmfwp/dmfwp.c b/private/windows/diamond/dmftools/dmfwp/dmfwp.c new file mode 100644 index 000000000..44dfe81a0 --- /dev/null +++ b/private/windows/diamond/dmftools/dmfwp/dmfwp.c @@ -0,0 +1,92 @@ +#include <windows.h> +#include <winioctl.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +int +_cdecl +main( + int argc, + char** argv + ) + +/*++ + +Routine Description: + + This routine writes the correct OEM field to the floppy, thus write-protecting + it. + +Arguments: + + argc - Supplies the number of command line arguments. + + argv - Supplies the command line arguments. + +Return Value: + + 0 - Success. + + 1 - Failure. + +--*/ + +{ + CHAR drive[20]; + HANDLE handle; + BOOL b; + PUCHAR sectorBuffer; + DWORD bytes; + + // First make sure that we have at least one parameter. + + if (argc < 2) { + printf("usage: %s drive:\n", argv[0]); + return(1); + } + + + // Open the drive for exclusive access. + + sprintf(drive, "\\\\.\\%s", argv[1]); + handle = CreateFile(drive, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + printf("can't open drive %s, error = %d\n", argv[1], GetLastError()); + return(1); + } + + // Read the boot sector. + + + + + // Write out the boot sector, the FAT, and the root directory. + + sectorBuffer = LocalAlloc(LMEM_FIXED, 512 + 0x1FF); + sectorBuffer = (PUCHAR) (((ULONG) (sectorBuffer + 0x1FF))&(~0x1FF)); + + b = ReadFile(handle, sectorBuffer, 512, &bytes, NULL); + if (!b || bytes != 512) { + printf("Could not read boot sector. Error = %d\n", GetLastError()); + return(1); + } + + sectorBuffer[3] = 'M'; + + if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) { + printf("Could not reset file pointer. Error = %d\n", GetLastError()); + return(1); + } + + b = WriteFile(handle, sectorBuffer, 512, &bytes, NULL); + if (!b || bytes != 512) { + printf("Could not write boot sector. Error = %d\n", GetLastError()); + return(1); + } + + CloseHandle(handle); + + return(0); +} diff --git a/private/windows/diamond/dmftools/dmfwp/makefile b/private/windows/diamond/dmftools/dmfwp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/windows/diamond/dmftools/dmfwp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/windows/diamond/dmftools/dmfwp/sources b/private/windows/diamond/dmftools/dmfwp/sources new file mode 100644 index 000000000..35a7eefcd --- /dev/null +++ b/private/windows/diamond/dmftools/dmfwp/sources @@ -0,0 +1,31 @@ +!IF 0 + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + +!ENDIF + +USE_CRTDLL=1 +MAJORCOMP= +MINORCOMP= + +TARGETNAME=dmfwp +TARGETPATH=obj +TARGETTYPE=PROGRAM + +SOURCES=dmfwp.c + +UMTYPE=console +UMAPPL=dmfwp +UMLIBS= + diff --git a/private/windows/diamond/dmftools/fcopy/bootsect.h b/private/windows/diamond/dmftools/fcopy/bootsect.h new file mode 100644 index 000000000..372460d1f --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/bootsect.h @@ -0,0 +1,135 @@ +UCHAR Nt31SetupBootSector[ 512 ] = { + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x02, 0xE0, 0x00, 0x40, 0x0B, 0xF0, 0x09, 0x00, + 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x4F, 0x20, 0x4E, 0x41, + 0x4D, 0x45, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, + 0x20, 0x20, 0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0x68, 0xC0, 0x07, + 0x1F, 0xA0, 0x10, 0x00, 0xF7, 0x26, 0x16, 0x00, 0x03, 0x06, 0x0E, 0x00, + 0x50, 0x91, 0xB8, 0x20, 0x00, 0xF7, 0x26, 0x11, 0x00, 0x8B, 0x1E, 0x0B, + 0x00, 0x03, 0xC3, 0x48, 0xF7, 0xF3, 0x03, 0xC8, 0x89, 0x0E, 0x08, 0x02, + 0x68, 0x00, 0x10, 0x07, 0x33, 0xDB, 0x8F, 0x06, 0x13, 0x02, 0x89, 0x1E, + 0x15, 0x02, 0x0E, 0xE8, 0x90, 0x00, 0x72, 0x57, 0x33, 0xDB, 0x8B, 0x0E, + 0x11, 0x00, 0x8B, 0xFB, 0x51, 0xB9, 0x0B, 0x00, 0xBE, 0xDC, 0x01, 0xF3, + 0xA6, 0x59, 0x74, 0x05, 0x83, 0xC3, 0x20, 0xE2, 0xED, 0xE3, 0x37, 0x26, + 0x8B, 0x57, 0x1A, 0x52, 0xB8, 0x01, 0x00, 0x68, 0x00, 0x20, 0x07, 0x33, + 0xDB, 0x0E, 0xE8, 0x48, 0x00, 0x72, 0x28, 0x5B, 0x8D, 0x36, 0x0B, 0x00, + 0x8D, 0x3E, 0x0B, 0x02, 0x1E, 0x8F, 0x45, 0x02, 0xC7, 0x05, 0xF5, 0x00, + 0x1E, 0x8F, 0x45, 0x06, 0xC7, 0x45, 0x04, 0x0E, 0x01, 0x8A, 0x16, 0x24, + 0x00, 0xEA, 0x03, 0x00, 0x00, 0x20, 0xBE, 0x86, 0x01, 0xEB, 0x03, 0xBE, + 0xA2, 0x01, 0xE8, 0x09, 0x00, 0xBE, 0xC1, 0x01, 0xE8, 0x03, 0x00, 0xFB, + 0xEB, 0xFE, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4, 0x0E, 0xBB, 0x07, 0x00, + 0xCD, 0x10, 0xEB, 0xF2, 0xC3, 0x50, 0x4A, 0x4A, 0xA0, 0x0D, 0x00, 0x32, + 0xE4, 0xF7, 0xE2, 0x03, 0x06, 0x08, 0x02, 0x83, 0xD2, 0x00, 0xA3, 0x13, + 0x02, 0x89, 0x16, 0x15, 0x02, 0x58, 0xA2, 0x07, 0x02, 0xA1, 0x13, 0x02, + 0x8B, 0x16, 0x15, 0x02, 0x03, 0x06, 0x1C, 0x00, 0x13, 0x16, 0x1E, 0x00, + 0xF7, 0x36, 0x18, 0x00, 0xFE, 0xC2, 0x88, 0x16, 0x06, 0x02, 0x33, 0xD2, + 0xF7, 0x36, 0x1A, 0x00, 0x88, 0x16, 0x25, 0x00, 0xA3, 0x04, 0x02, 0xA1, + 0x18, 0x00, 0x2A, 0x06, 0x06, 0x02, 0x40, 0x3A, 0x06, 0x07, 0x02, 0x76, + 0x05, 0xA0, 0x07, 0x02, 0x32, 0xE4, 0x50, 0xB4, 0x02, 0x8B, 0x0E, 0x04, + 0x02, 0xC0, 0xE5, 0x06, 0x0A, 0x2E, 0x06, 0x02, 0x86, 0xE9, 0x8B, 0x16, + 0x24, 0x00, 0xCD, 0x13, 0x0F, 0x83, 0x05, 0x00, 0x83, 0xC4, 0x02, 0xF9, + 0xCB, 0x58, 0x28, 0x06, 0x07, 0x02, 0x76, 0x11, 0x01, 0x06, 0x13, 0x02, + 0x83, 0x16, 0x15, 0x02, 0x00, 0xF7, 0x26, 0x0B, 0x00, 0x03, 0xD8, 0xEB, + 0x90, 0xA2, 0x07, 0x02, 0xF8, 0xCB, 0x42, 0x4F, 0x4F, 0x54, 0x3A, 0x20, + 0x43, 0x6F, 0x75, 0x6C, 0x64, 0x6E, 0x27, 0x74, 0x20, 0x66, 0x69, 0x6E, + 0x64, 0x20, 0x4E, 0x54, 0x4C, 0x44, 0x52, 0x0D, 0x0A, 0x00, 0x42, 0x4F, + 0x4F, 0x54, 0x3A, 0x20, 0x49, 0x2F, 0x4F, 0x20, 0x65, 0x72, 0x72, 0x6F, + 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x64, 0x69, + 0x73, 0x6B, 0x0D, 0x0A, 0x00, 0x50, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, + 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x6E, 0x6F, 0x74, 0x68, + 0x65, 0x72, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x00, 0x53, 0x45, 0x54, 0x55, + 0x50, 0x4C, 0x44, 0x52, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA }; + +UCHAR Nt35SetupBootSector[ 512 ] = { + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x02, 0xE0, 0x00, 0x40, 0x0B, 0xF0, 0x09, 0x00, + 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x4F, 0x20, 0x4E, 0x41, + 0x4D, 0x45, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, + 0x20, 0x20, 0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0x68, 0xC0, 0x07, + 0x1F, 0xA0, 0x10, 0x00, 0xF7, 0x26, 0x16, 0x00, 0x03, 0x06, 0x0E, 0x00, + 0x50, 0x91, 0xB8, 0x20, 0x00, 0xF7, 0x26, 0x11, 0x00, 0x8B, 0x1E, 0x0B, + 0x00, 0x03, 0xC3, 0x48, 0xF7, 0xF3, 0x03, 0xC8, 0x89, 0x0E, 0x08, 0x02, + 0x68, 0x00, 0x10, 0x07, 0x33, 0xDB, 0x8F, 0x06, 0x13, 0x02, 0x89, 0x1E, + 0x15, 0x02, 0x0E, 0xE8, 0x90, 0x00, 0x72, 0x57, 0x33, 0xDB, 0x8B, 0x0E, + 0x11, 0x00, 0x8B, 0xFB, 0x51, 0xB9, 0x0B, 0x00, 0xBE, 0xDC, 0x01, 0xF3, + 0xA6, 0x59, 0x74, 0x05, 0x83, 0xC3, 0x20, 0xE2, 0xED, 0xE3, 0x37, 0x26, + 0x8B, 0x57, 0x1A, 0x52, 0xB8, 0x01, 0x00, 0x68, 0x00, 0x20, 0x07, 0x33, + 0xDB, 0x0E, 0xE8, 0x48, 0x00, 0x72, 0x28, 0x5B, 0x8D, 0x36, 0x0B, 0x00, + 0x8D, 0x3E, 0x0B, 0x02, 0x1E, 0x8F, 0x45, 0x02, 0xC7, 0x05, 0xF5, 0x00, + 0x1E, 0x8F, 0x45, 0x06, 0xC7, 0x45, 0x04, 0x0E, 0x01, 0x8A, 0x16, 0x24, + 0x00, 0xEA, 0x03, 0x00, 0x00, 0x20, 0xBE, 0x86, 0x01, 0xEB, 0x03, 0xBE, + 0xA2, 0x01, 0xE8, 0x09, 0x00, 0xBE, 0xC1, 0x01, 0xE8, 0x03, 0x00, 0xFB, + 0xEB, 0xFE, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4, 0x0E, 0xBB, 0x07, 0x00, + 0xCD, 0x10, 0xEB, 0xF2, 0xC3, 0x50, 0x4A, 0x4A, 0xA0, 0x0D, 0x00, 0x32, + 0xE4, 0xF7, 0xE2, 0x03, 0x06, 0x08, 0x02, 0x83, 0xD2, 0x00, 0xA3, 0x13, + 0x02, 0x89, 0x16, 0x15, 0x02, 0x58, 0xA2, 0x07, 0x02, 0xA1, 0x13, 0x02, + 0x8B, 0x16, 0x15, 0x02, 0x03, 0x06, 0x1C, 0x00, 0x13, 0x16, 0x1E, 0x00, + 0xF7, 0x36, 0x18, 0x00, 0xFE, 0xC2, 0x88, 0x16, 0x06, 0x02, 0x33, 0xD2, + 0xF7, 0x36, 0x1A, 0x00, 0x88, 0x16, 0x25, 0x00, 0xA3, 0x04, 0x02, 0xA1, + 0x18, 0x00, 0x2A, 0x06, 0x06, 0x02, 0x40, 0x3A, 0x06, 0x07, 0x02, 0x76, + 0x05, 0xA0, 0x07, 0x02, 0x32, 0xE4, 0x50, 0xB4, 0x02, 0x8B, 0x0E, 0x04, + 0x02, 0xC0, 0xE5, 0x06, 0x0A, 0x2E, 0x06, 0x02, 0x86, 0xE9, 0x8B, 0x16, + 0x24, 0x00, 0xCD, 0x13, 0x0F, 0x83, 0x05, 0x00, 0x83, 0xC4, 0x02, 0xF9, + 0xCB, 0x58, 0x28, 0x06, 0x07, 0x02, 0x76, 0x11, 0x01, 0x06, 0x13, 0x02, + 0x83, 0x16, 0x15, 0x02, 0x00, 0xF7, 0x26, 0x0B, 0x00, 0x03, 0xD8, 0xEB, + 0x90, 0xA2, 0x07, 0x02, 0xF8, 0xCB, 0x42, 0x4F, 0x4F, 0x54, 0x3A, 0x20, + 0x43, 0x6F, 0x75, 0x6C, 0x64, 0x6E, 0x27, 0x74, 0x20, 0x66, 0x69, 0x6E, + 0x64, 0x20, 0x4E, 0x54, 0x4C, 0x44, 0x52, 0x0D, 0x0A, 0x00, 0x42, 0x4F, + 0x4F, 0x54, 0x3A, 0x20, 0x49, 0x2F, 0x4F, 0x20, 0x65, 0x72, 0x72, 0x6F, + 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x64, 0x69, + 0x73, 0x6B, 0x0D, 0x0A, 0x00, 0x50, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, + 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x6E, 0x6F, 0x74, 0x68, + 0x65, 0x72, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x00, 0x53, 0x45, 0x54, 0x55, + 0x50, 0x4C, 0x44, 0x52, 0x42, 0x49, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA }; + +UCHAR Dos5BootSector[ 512 ] = { + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x02, 0xE0, 0x00, 0x40, 0x0B, 0xF0, 0x09, 0x00, + 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x29, 0x00, 0x12, 0x61, 0x10, 0x4E, 0x4F, 0x20, 0x4E, 0x41, + 0x4D, 0x45, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, + 0x20, 0x20, 0xFA, 0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0x16, 0x07, + 0xBB, 0x78, 0x00, 0x36, 0xC5, 0x37, 0x1E, 0x56, 0x16, 0x53, 0xBF, 0x3E, + 0x7C, 0xB9, 0x0B, 0x00, 0xFC, 0xF3, 0xA4, 0x06, 0x1F, 0xC6, 0x45, 0xFE, + 0x0F, 0x8B, 0x0E, 0x18, 0x7C, 0x88, 0x4D, 0xF9, 0x89, 0x47, 0x02, 0xC7, + 0x07, 0x3E, 0x7C, 0xFB, 0xCD, 0x13, 0x72, 0x79, 0x33, 0xC0, 0x39, 0x06, + 0x13, 0x7C, 0x74, 0x08, 0x8B, 0x0E, 0x13, 0x7C, 0x89, 0x0E, 0x20, 0x7C, + 0xA0, 0x10, 0x7C, 0xF7, 0x26, 0x16, 0x7C, 0x03, 0x06, 0x1C, 0x7C, 0x13, + 0x16, 0x1E, 0x7C, 0x03, 0x06, 0x0E, 0x7C, 0x83, 0xD2, 0x00, 0xA3, 0x50, + 0x7C, 0x89, 0x16, 0x52, 0x7C, 0xA3, 0x49, 0x7C, 0x89, 0x16, 0x4B, 0x7C, + 0xB8, 0x20, 0x00, 0xF7, 0x26, 0x11, 0x7C, 0x8B, 0x1E, 0x0B, 0x7C, 0x03, + 0xC3, 0x48, 0xF7, 0xF3, 0x01, 0x06, 0x49, 0x7C, 0x83, 0x16, 0x4B, 0x7C, + 0x00, 0xBB, 0x00, 0x05, 0x8B, 0x16, 0x52, 0x7C, 0xA1, 0x50, 0x7C, 0xE8, + 0x92, 0x00, 0x72, 0x1D, 0xB0, 0x01, 0xE8, 0xAC, 0x00, 0x72, 0x16, 0x8B, + 0xFB, 0xB9, 0x0B, 0x00, 0xBE, 0xE6, 0x7D, 0xF3, 0xA6, 0x75, 0x0A, 0x8D, + 0x7F, 0x20, 0xB9, 0x0B, 0x00, 0xF3, 0xA6, 0x74, 0x18, 0xBE, 0x9E, 0x7D, + 0xE8, 0x5F, 0x00, 0x33, 0xC0, 0xCD, 0x16, 0x5E, 0x1F, 0x8F, 0x04, 0x8F, + 0x44, 0x02, 0xCD, 0x19, 0x58, 0x58, 0x58, 0xEB, 0xE8, 0x8B, 0x47, 0x1A, + 0x48, 0x48, 0x8A, 0x1E, 0x0D, 0x7C, 0x32, 0xFF, 0xF7, 0xE3, 0x03, 0x06, + 0x49, 0x7C, 0x13, 0x16, 0x4B, 0x7C, 0xBB, 0x00, 0x07, 0xB9, 0x03, 0x00, + 0x50, 0x52, 0x51, 0xE8, 0x3A, 0x00, 0x72, 0xD8, 0xB0, 0x01, 0xE8, 0x54, + 0x00, 0x59, 0x5A, 0x58, 0x72, 0xBB, 0x05, 0x01, 0x00, 0x83, 0xD2, 0x00, + 0x03, 0x1E, 0x0B, 0x7C, 0xE2, 0xE2, 0x8A, 0x2E, 0x15, 0x7C, 0x8A, 0x16, + 0x24, 0x7C, 0x8B, 0x1E, 0x49, 0x7C, 0xA1, 0x4B, 0x7C, 0xEA, 0x00, 0x00, + 0x70, 0x00, 0xAC, 0x0A, 0xC0, 0x74, 0x29, 0xB4, 0x0E, 0xBB, 0x07, 0x00, + 0xCD, 0x10, 0xEB, 0xF2, 0x3B, 0x16, 0x18, 0x7C, 0x73, 0x19, 0xF7, 0x36, + 0x18, 0x7C, 0xFE, 0xC2, 0x88, 0x16, 0x4F, 0x7C, 0x33, 0xD2, 0xF7, 0x36, + 0x1A, 0x7C, 0x88, 0x16, 0x25, 0x7C, 0xA3, 0x4D, 0x7C, 0xF8, 0xC3, 0xF9, + 0xC3, 0xB4, 0x02, 0x8B, 0x16, 0x4D, 0x7C, 0xB1, 0x06, 0xD2, 0xE6, 0x0A, + 0x36, 0x4F, 0x7C, 0x8B, 0xCA, 0x86, 0xE9, 0x8A, 0x16, 0x24, 0x7C, 0x8A, + 0x36, 0x25, 0x7C, 0xCD, 0x13, 0xC3, 0x0D, 0x0A, 0x4E, 0x6F, 0x6E, 0x2D, + 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, + 0x6F, 0x72, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x65, 0x72, 0x72, 0x6F, + 0x72, 0x0D, 0x0A, 0x52, 0x65, 0x70, 0x6C, 0x61, 0x63, 0x65, 0x20, 0x61, + 0x6E, 0x64, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, + 0x20, 0x6B, 0x65, 0x79, 0x20, 0x77, 0x68, 0x65, 0x6E, 0x20, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x0D, 0x0A, 0x00, 0x49, 0x4F, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x53, 0x59, 0x53, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x20, 0x20, + 0x20, 0x53, 0x59, 0x53, 0x00, 0x00, 0x55, 0xAA }; + diff --git a/private/windows/diamond/dmftools/fcopy/dmffmt.c b/private/windows/diamond/dmftools/fcopy/dmffmt.c new file mode 100644 index 000000000..7e57fb6ef --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/dmffmt.c @@ -0,0 +1,106 @@ +#include "precomp.h" +#pragma hdrstop + +BOOL +DmfFormatTracks( + HANDLE hDevice, + DWORD *pdwGLE + ) + +/*++ + +Routine Description: + + Function to format the tracks for a 3.5" HD floppy using the DMF + format (1.8MB). This function DOES NOT lay down the boot sector. + +Arguments: + + hDevice - supplies the handle of the floppy that has been opened for + exclusive DASD access. + + pdwGLE - receives the error code returned by GetLastError() if a + problem is encountered. + +Return Value: + + FALSE if an error occurred while formatting the floppy. If so, dwGLE + will contain the error code. + + TRUE otherwise. + +--*/ + +{ +#define FORMAT_PARAMETERS_SIZE (sizeof(FORMAT_EX_PARAMETERS) + 20*sizeof(USHORT)) + + CHAR formatParametersBuffer[FORMAT_PARAMETERS_SIZE]; + PFORMAT_EX_PARAMETERS formatParameters; + ULONG i, next; + BOOL b; + DWORD bytesWritten; + USHORT swapBuffer[3]; + + // Prepare the format parameters. + + formatParameters = (PFORMAT_EX_PARAMETERS) formatParametersBuffer; + formatParameters->MediaType = F3_1Pt44_512; + formatParameters->FormatGapLength = 8; + formatParameters->SectorsPerTrack = 21; + + next = 0; + for (i = 0; i < formatParameters->SectorsPerTrack; i += 2) { + formatParameters->SectorNumber[i] = (USHORT) (i/2 + 1); + next++; + } + + for (i = 1; i < formatParameters->SectorsPerTrack; i += 2) { + formatParameters->SectorNumber[i] = (USHORT) (i/2 + next + 1); + } + + // Start off by putting the boot sector as the last sector of + // the first track. + + MoveMemory(&formatParameters->SectorNumber[0], + &formatParameters->SectorNumber[1], + 20*sizeof(USHORT)); + formatParameters->SectorNumber[20] = 1; + + + // Format each track on the floppy. + + for (i = 0; i < 80; i++) { + + formatParameters->StartCylinderNumber = + formatParameters->EndCylinderNumber = i; + formatParameters->StartHeadNumber = 0; + formatParameters->EndHeadNumber = 1; + + // printf("Formatting track %d\r", i); + + b = DeviceIoControl(hDevice, IOCTL_DISK_FORMAT_TRACKS_EX, + formatParameters, FORMAT_PARAMETERS_SIZE, + NULL, 0, &bytesWritten, NULL); + + if (!b) { + *pdwGLE = GetLastError(); + return FALSE; + } + + + // Skew the next cylinder by 3 sectors from this one. + + MoveMemory(swapBuffer, + &formatParameters->SectorNumber[18], + 3*sizeof(USHORT)); + MoveMemory(&formatParameters->SectorNumber[3], + &formatParameters->SectorNumber[0], + 18*sizeof(USHORT)); + MoveMemory(&formatParameters->SectorNumber[0], + swapBuffer, + 3*sizeof(USHORT)); + + } + return TRUE; +} + diff --git a/private/windows/diamond/dmftools/fcopy/dmffmt.h b/private/windows/diamond/dmftools/fcopy/dmffmt.h new file mode 100644 index 000000000..977d99484 --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/dmffmt.h @@ -0,0 +1,6 @@ +BOOL +DmfFormatTracks( + HANDLE hDevice, + DWORD *pdwGLE + ); + diff --git a/private/windows/diamond/dmftools/fcopy/fcopy.c b/private/windows/diamond/dmftools/fcopy/fcopy.c new file mode 100644 index 000000000..f390fdda3 --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/fcopy.c @@ -0,0 +1,2759 @@ + +///////////////////////////////////////////////////////////////////////////// +// // +// fcopy.c Program for mastering floppy disks, either as an // +// image file or directly writing the floppy disk. // +// // +// Code is targeted as Win32 console application. // +// // +// Author: Tom McGuire (tommcg) // +// // +// Original version written June 1993. // +// // +// (C) Copyright 1993-1996, Microsoft Corporation // +// // +///////////////////////////////////////////////////////////////////////////// + + +#include "precomp.h" +#pragma hdrstop +#include "bootsect.h" + +#define dwShow( dwValue ) printf( #dwValue " = 0x%08X\n", (dwValue) ) + +#define NEW_NT_EXTENDED_FAT_DIRECTORY_ENTRY_INTERPRETATION 1 + +#define MAX_FAT_ENTRIES 0xC00 // 3.5" HD 1.44MB floppy +#define MAX_FAT_NAME 14 // 8.3 plus NULL, dot, and one extra + +// +// MAX_ROOT_ENTRIES is no longer a constant +// +#define MAX_ROOT_ENTRIES_STD 224 // 224 * 32bytes = 0x1C00 bytes total root +#define MAX_ROOT_ENTRIES_DMF 16 // 16 * 32 = 0x200 bytes total root (1 sector) +WORD wMaxRootEntries; + +#define MAX_ENV 1024 // max size of environment variable we'll handle + +#define DISKTYPE_35 35 +#define DISKTYPE_525 525 +#define DISKTYPE_DMF 18 +#define DISKTYPE_UNKNOWN 0 + +// +// CLUSTER_SIZE is no longer a constant +// +#define SECTOR_SIZE 512 +#define CLUSTER_SIZE_STD 512 // 1 sector/cluster +#define CLUSTER_SIZE_DMF 1024 // 2 sectors/cluster +WORD wClusterSize; + +#define BUFFER_SIZE 0x10000 + +#define DOS_ATTRIBUTE_NORMAL 0x00 +#define DOS_ATTRIBUTE_READONLY 0x01 +#define DOS_ATTRIBUTE_HIDDEN 0x02 +#define DOS_ATTRIBUTE_SYSTEM 0x04 +#define DOS_ATTRIBUTE_VOLUME 0x08 +#define DOS_ATTRIBUTE_DIRECTORY 0x10 +#define DOS_ATTRIBUTE_ARCHIVE 0x20 + +#define FILE_ATTRIBUTE_NOT_NORMAL ( FILE_ATTRIBUTE_HIDDEN | \ + FILE_ATTRIBUTE_SYSTEM | \ + FILE_ATTRIBUTE_DIRECTORY ) + +#define ROUNDUP2( x, n ) (((x) + ((n) - 1 )) & ~((n) - 1 )) + +#pragma pack( 1 ) // following is an on-disk structure, no padding + +typedef struct _DIRENTRY { + CHAR BaseName[ 8 ]; + CHAR Extension[ 3 ]; + BYTE Attribute; + union { + BYTE Reserved[ 10 ]; + struct { + BYTE NtByte; + BYTE CreationTimeMilliSeconds; + WORD CreationTime; + WORD CreationDate; + WORD LastAccessDate; + WORD ExtendedAttributes; + } Extended; + }; + WORD TimeStamp; // LastWriteTime + WORD DateStamp; // LastWriteDate + WORD Cluster; + DWORD FileSize; + } DIRENTRY, *PDIRENTRY; + +#pragma pack() + +typedef struct _FILENODE { + LPSTR pszTargetFileName; // malloc'd, unique to node, looks like "foo.txt" + LPSTR pszSourceFileName; // malloc'd, unique to node, looks like "foo.txt" + LPSTR pszSourcePath; // malloc'd, shared by nodes, looks like "d:\foo\" + DWORD dwFileSize; + DWORD dwStartingCluster; + DWORD dwWin32Attributes; + FILETIME ftWin32TimeStamp; // last write time + UINT nSourceIndex; + struct _FILENODE * pParentDir; + struct _FILENODE * pNextFile; + struct _FILENODE * pFirstFile; + } FILENODE, *PFILENODE; + +typedef struct _PARAM { + LPSTR pszParam; + BOOL bRecurse; + LPSTR pszTargetDir; + struct _PARAM *pNext; + } PARAM, *PPARAM; + +PPARAM LoadParameters( int argc, char *argv[] ); +PPARAM RemoveParam( PPARAM pParam, PPARAM pPrev ); +PPARAM ParseResponseFile( LPSTR pszFileName, PPARAM pLast ); +void ParseOptions( PPARAM pParamList ); +void ParseDestination( PPARAM pParamList ); +void ParseSources( PPARAM pParamList ); +UINT ParseSourceParameter( PFILENODE pParentDir, + LPSTR pszSourcePath, + LPSTR pszSourceFileName, + LPSTR pszTargetFileName, + BOOL bRecursive, + UINT nSourceIndex ); +void ParseTimeStamp( LPSTR pArg ); +void PrepareStructures( void ); +void WriteDestination( void ); +void ReWriteDestination( LPSTR pszMessage ); +void VerifyDestination( void ); +void FormatTarget( void ); +BOOL AddFileToList( WIN32_FIND_DATA *fd, + PFILENODE pParentDir, + LPSTR pszSourcePath, + LPSTR pszTargetFileName, + UINT nSourceIndex, + PFILENODE *pThisNode ); +BOOL AddDirectoryToList( LPSTR pszPathName, + PFILENODE pParentDir, + UINT nSourceIndex, + PFILENODE *pThisNode ); +UINT CopyFileContents( HANDLE hTarget, HANDLE hSource, DWORD dwSize ); +void ErrorMessage( const char *szFormat, ... ); +void ErrorExit( const char *szFormat, ... ); +void Usage( void ); +void Copyright( void ); +BOOL IsDigit( char ch ); +UINT Min( UINT a, UINT b ); +void GetBootSector( void ); +DWORD CompareAligned( PVOID pBuffer1, PVOID pBuffer2, DWORD dwLength ); +PVOID MyAlloc( UINT nBytes ); +PVOID MyAllocZ( UINT nBytes ); +VOID MyFree( PVOID pAlloc ); +BOOL IsFileNameDotOrDotDot( LPCSTR pszFileName ); +PFILENODE NewFileNode( void ); +LPSTR DuplicateString( LPCSTR pString ); +void AssignDirectoryClusters( PFILENODE pDir ); +void AssignFileClusters( PFILENODE pDir ); +DWORD AllocateFat( DWORD dwFileSize ); +void PadCopy( LPSTR pDest, LPCSTR pSource, UINT nLength ); +UINT CreateAndWriteDirectories( PFILENODE pDir ); +UINT WalkListAndWriteFiles( PFILENODE pDir ); +UINT OpenAndCopySourceFile( PFILENODE pNode ); +void CreateVolumeLabelEntry( PDIRENTRY pDirEntry ); +void CreateTagFileEntry( PDIRENTRY pDirEntry ); +void CreateDirEntry( PDIRENTRY pDirEntry, + LPSTR pszFileName, + DWORD dwWin32Attributes, + FILETIME ftWin32TimeStamp, + DWORD dwStartingCluster, + DWORD dwFileSize ); +BOOL TrimTheTree( PFILENODE pDir ); +void InitializeOverlapped( void ); +void OverlappedVerifyComplete( HANDLE hFile ); +void OverlappedWriteFile( HANDLE hFile, LPVOID pData, DWORD nBytes ); +void OverlappedReadFile( HANDLE hFile, LPVOID pData, DWORD nBytes, PDWORD pdwActualBytes ); +void OverlappedSetFilePointer( HANDLE hFile, DWORD dwAbsoluteOffset ); +LPSTR ReplaceEnvironmentStrings( LPCSTR pszInputString, LPSTR pTargetBuffer ); +void BuildFullTargetName( PFILENODE pNode, LPSTR pBuffer ); +void AssignDmfSignature( PUCHAR pBuffer ); + +USHORT Fat[ MAX_FAT_ENTRIES ]; + +CHAR chFatImage[ 0x1200 ]; + +UCHAR BootSector[ SECTOR_SIZE ]; + +PFILENODE pRootDir; + +UINT nMaxFatEntries; + +UINT nNextAvailableCluster; + +CHAR chDest[ MAX_PATH ]; +CHAR chLabel[ MAX_FAT_NAME ]; +CHAR chTagFile[ MAX_FAT_NAME ]; +CHAR chBootSpecifier[ MAX_PATH ]; + +PVOID pAlignedBuffer; + +HANDLE hTarget, hDevice; + +PUCHAR pWholeDiskBuffer, pWholeDiskCurrentLocation; + +DWORD fDiskType = DISKTYPE_UNKNOWN; + +BOOL bDestIsDevice; +BOOL bZeroSlack; +BOOL bUseGlobalTime; +BOOL bDoubleWrite; +BOOL bFormatTarget; +BOOL bVerifyTarget; +BOOL bIncludeEmptyDirectories; +BOOL bCopyAttributes; +BOOL bFormatTargetDMF; +BOOL bWritableDMF; +BOOL bWriteToTarget; +BOOL bWriteToBuffer; + +WORD wDosDate; +WORD wDosTime; + +PPARAM pParamList; + +HANDLE hProcessHeap; +UINT nMaxSourceLength; + +OVERLAPPED olControl; +DWORD dwOverlappedFilePointer; +DWORD dwOverlappedExpectedBytes; +BOOL bOverlappedOutstanding; +BOOL bOverlappedWriting; +PVOID pOverlappedBuffer[ 2 ]; +INT iAvailableOverlappedBuffer; + +SYSTEMTIME stGlobalSystemTime; +FILETIME ftGlobalFileTime; + + +void _cdecl main( int argc, char *argv[] ) { + + Copyright(); + + if ( argc < 3 ) + Usage(); + + SetFileApisToOEM(); + + SetErrorMode( SEM_FAILCRITICALERRORS ); + + hProcessHeap = GetProcessHeap(); + + GetLocalTime( &stGlobalSystemTime ); + SystemTimeToFileTime( &stGlobalSystemTime, &ftGlobalFileTime ); + FileTimeToDosDateTime( &ftGlobalFileTime, &wDosDate, &wDosTime ); + + + pAlignedBuffer = VirtualAlloc( NULL, BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE ); + if ( pAlignedBuffer == NULL ) + ErrorExit( "\nOut of memory\n" ); + + pParamList = LoadParameters( argc, argv ); + + ParseOptions( pParamList ); + + ParseDestination( pParamList ); + + if ( (bFormatTarget || bFormatTargetDMF) && bDestIsDevice ) + FormatTarget(); + + pWholeDiskBuffer = VirtualAlloc( NULL, 0x1C0000, MEM_COMMIT, PAGE_READWRITE ); + if ( pWholeDiskBuffer == NULL ) + ErrorExit( "\nOut of memory\n" ); + pWholeDiskCurrentLocation = pWholeDiskBuffer; + + ParseSources( pParamList ); + + PrepareStructures(); + + WriteDestination(); + + exit( 0 ); + + } + + +void WriteDestination( void ) { + + DWORD dwBytes, nClusters, nBuffers; + UINT i, last; + +#ifdef DONTCOMPILE // now leaving hDevice open as global + hTarget = CreateFile( chDest, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + bDestIsDevice ? OPEN_EXISTING + : CREATE_ALWAYS, + bDestIsDevice ? FILE_FLAG_NO_BUFFERING | + FILE_FLAG_OVERLAPPED + : FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_OVERLAPPED, + NULL ); + + if ( hTarget == INVALID_HANDLE_VALUE ) + ErrorExit( "\nCreateFile( \"%s\" ) failed (GLE=%d)\n", + chDest, + GetLastError() ); +#endif + + if ( bDestIsDevice ) + hTarget = hDevice; + + else { + + hTarget = CreateFile( chDest, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL ); + + if ( hTarget == INVALID_HANDLE_VALUE ) + ErrorExit( "\nCreateFile( \"%s\" ) failed (GLE=%d)\n", + chDest, + GetLastError() ); + } + + InitializeOverlapped(); + + if ( fDiskType == DISKTYPE_DMF ) { + bWriteToTarget = FALSE; + bWriteToBuffer = TRUE; + bZeroSlack = TRUE; + } + else { + bWriteToTarget = TRUE; + bWriteToBuffer = ( bVerifyTarget || ( bDoubleWrite && bDestIsDevice )); + } + + OverlappedWriteFile( hTarget, BootSector, SECTOR_SIZE ); + + if ( fDiskType == DISKTYPE_35 ) { + dwBytes = 0x1200; + } else if ( fDiskType == DISKTYPE_525 ) { + dwBytes = 0xE00; + } else { // else we're using DMF format + dwBytes = 0xA00; + } + + OverlappedWriteFile( hTarget, chFatImage, dwBytes ); + OverlappedWriteFile( hTarget, chFatImage, dwBytes ); + + nClusters = 1; + + nClusters += CreateAndWriteDirectories( pRootDir ); + + ErrorMessage( "done\n" ); // "creating directories" + + ErrorMessage( "Copying files...\n" ); + + nClusters += WalkListAndWriteFiles( pRootDir ); + + if ( nNextAvailableCluster != ( nClusters + 1 )) { + ErrorExit( "\nASSERTION FAILURE: Cluster count mismatch:\n" + " nClusters = %d\n" + " nNextAvailableCluster = %d\n", + nClusters, + nNextAvailableCluster ); + } + + if ( bZeroSlack ) { + + if ( fDiskType == DISKTYPE_35 ) { + last = 2848; + } else if ( fDiskType == DISKTYPE_525 ) { + last = 2372; + } else { // (fDiskType == DISKTYPE_DMF) + last = 1675; + } + + if ( nClusters < last ) { + + ErrorMessage( "Zeroing slack space..." ); + + dwBytes = ( last - nClusters ) * wClusterSize; + nBuffers = ROUNDUP2( dwBytes, BUFFER_SIZE ) / BUFFER_SIZE; + + ZeroMemory( pAlignedBuffer, Min( dwBytes, BUFFER_SIZE )); + + for ( i = 0; i < nBuffers; i++ ) { + OverlappedWriteFile( hTarget, pAlignedBuffer, Min( dwBytes, BUFFER_SIZE )); + dwBytes -= BUFFER_SIZE; + } + + ErrorMessage( "done\n" ); + + } + } + + bWriteToBuffer = FALSE; + bWriteToTarget = TRUE; + + if ( fDiskType == DISKTYPE_DMF ) { +#ifdef DMFSIGNATURE + AssignDmfSignature( pWholeDiskBuffer ); +#endif + ReWriteDestination( "Writing image to target..." ); + } + + if ( bDoubleWrite && bDestIsDevice ) { + ReWriteDestination( "Rewriting each sector..." ); + } + + if ( bVerifyTarget ) + VerifyDestination(); + + OverlappedVerifyComplete( hTarget ); + + CloseHandle( hTarget ); + + } + + +UINT CopyFileContents( HANDLE hTarget, HANDLE hSource, DWORD dwSize ) { + + DWORD dwBytes, dwClusterBytes; + UINT i, nClusters, nBuffers; + + nClusters = ROUNDUP2( dwSize, wClusterSize ) / wClusterSize; + nBuffers = ROUNDUP2( dwSize, BUFFER_SIZE ) / BUFFER_SIZE; + + for ( i = 0; i < nBuffers; i++ ) { + + if ( ! ReadFile( hSource, pAlignedBuffer, BUFFER_SIZE, &dwBytes, NULL )) + ErrorExit( "\nError reading file (GLE=%d)\n", GetLastError() ); + + if ( dwBytes < BUFFER_SIZE ) { + dwClusterBytes = ROUNDUP2( dwBytes, wClusterSize ); + ZeroMemory( (PCHAR)pAlignedBuffer + dwBytes, dwClusterBytes - dwBytes ); + dwBytes = dwClusterBytes; + } + + OverlappedWriteFile( hTarget, pAlignedBuffer, dwBytes ); + + } + + return nClusters; + + } + + +void PrepareStructures( void ) { + + UINT i; + CHAR *p; + + ErrorMessage( "Creating directories..." ); + + GetBootSector(); + + // + // Choose serial number from tick count + // + + *(UNALIGNED DWORD*)&BootSector[ 0x27 ] = GetTickCount(); + + if ( *chLabel != '\0' ) { + memcpy( &BootSector[ 0x2B ], chLabel, Min( strlen( chLabel ), 11 )); + } + + if ( fDiskType == DISKTYPE_35 ) { + + Fat[ 0 ] = 0xFF0; + nMaxFatEntries = 0xB20; + *(UNALIGNED WORD*)&BootSector[ 0x13 ] = 0xB40; + *(UNALIGNED BYTE*)&BootSector[ 0x15 ] = 0xF0; + *(UNALIGNED WORD*)&BootSector[ 0x16 ] = 0x09; + *(UNALIGNED WORD*)&BootSector[ 0x18 ] = 0x12; + + } else if ( fDiskType == DISKTYPE_525 ) { + + Fat[ 0 ] = 0xFF9; + nMaxFatEntries = 0x944; + *(UNALIGNED WORD*)&BootSector[ 0x13 ] = 0x960; + *(UNALIGNED BYTE*)&BootSector[ 0x15 ] = 0xF9; + *(UNALIGNED WORD*)&BootSector[ 0x16 ] = 0x07; + *(UNALIGNED WORD*)&BootSector[ 0x18 ] = 0x0F; + + } else if (fDiskType == DISKTYPE_DMF ) { + + Fat[ 0 ] = 0xFF0; + nMaxFatEntries = 0x68C; + + // + // Write OEM Name & Version, which is special to DMF. If the + // signature is exactly "MSDMF3.2", then it will be treated + // as write-protected which is what we want. If -w option + // (writable DMF) has been specified, then we'll tweak this + // string to "NSDMF3.2" which will make it un-write-protected. + // + + CopyMemory( (UNALIGNED BYTE*)&BootSector[ 0x03 ], "MSDMF3.2", 8 ); + + if ( bWritableDMF ) { + *(UNALIGNED BYTE*)&BootSector[ 0x03 ] = 'N'; + } + + *(UNALIGNED WORD*)&BootSector[ 0x0B ] = 0x200; // Bytes Per Sector + *(UNALIGNED BYTE*)&BootSector[ 0x0D ] = 0x02; // Sectors Per Cluster + *(UNALIGNED WORD*)&BootSector[ 0x0E ] = 0x01; // Reserved Sectors + *(UNALIGNED BYTE*)&BootSector[ 0x10 ] = 0x02; // Number of Copies of FAT + *(UNALIGNED WORD*)&BootSector[ 0x11 ] = 0x10; // Max. Number of Root Dir Entries + *(UNALIGNED WORD*)&BootSector[ 0x13 ] = 0xD20; // Total Sectors + *(UNALIGNED BYTE*)&BootSector[ 0x15 ] = 0xF0; // Media Descriptor Byte + *(UNALIGNED WORD*)&BootSector[ 0x16 ] = 0x05; // Sectors Per FAT + *(UNALIGNED WORD*)&BootSector[ 0x18 ] = 0x15; // Sectors Per Track + + + } else { + ErrorExit( "\nUnknown target disk type\n" ); + } + + Fat[ 1 ] = 0xFFF; + + nNextAvailableCluster = 2; + + AssignDirectoryClusters( pRootDir ); + + AssignFileClusters( pRootDir ); + + for ( i = 0, p = chFatImage; i < nNextAvailableCluster; i += 2, p += 3 ) + *(UNALIGNED DWORD *)p = Fat[ i ] | ( Fat[ i + 1 ] << 12 ); + + } + + +void ParseDestination( PPARAM pParamList ) { + + // + // The destination parameter is required and is always the + // last parameter on the command line. We'll remove it + // after parsing it so the ParseSources routine won't use it. + // + + PPARAM pPrev, pParam; + + if ( pParamList->pNext == NULL ) + ErrorExit( "\nNo destination parameter specified\n" ); + + for ( pPrev = pParamList, pParam = pParamList->pNext; + pParam->pNext != NULL; + pPrev = pParam, pParam = pParam->pNext ); + + strcpy( chDest, pParam->pszParam ); + MyFree( pParam ); + pPrev->pNext = NULL; + + if ( ! _stricmp( chDest, "A:" )) { + strcpy( chDest, "\\\\.\\A:" ); + bDestIsDevice = TRUE; + } + else if ( ! _stricmp( chDest, "B:" )) { + strcpy( chDest, "\\\\.\\B:" ); + bDestIsDevice = TRUE; + } + else { + bDestIsDevice = FALSE; + bZeroSlack = TRUE; + } + + if ( bDestIsDevice ) { + + DISK_GEOMETRY dgArray[ 20 ]; + DISK_GEOMETRY dg; + DWORD dwActual; + DWORD dwGLE; + UINT n, nElements; + BOOL bSuccess; + + hDevice = CreateFile( chDest, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if ( hDevice == INVALID_HANDLE_VALUE ) + ErrorExit( "\nCould not open device %s (GLE=%d)\n", + chDest + 4, + GetLastError() ); + + bSuccess = DeviceIoControl( hDevice, + IOCTL_DISK_GET_MEDIA_TYPES, + NULL, + 0, + &dgArray, + sizeof( dgArray ), + &dwActual, + NULL ); + + if ( ! bSuccess ) { + + if (( dwGLE = GetLastError() ) == ERROR_NOT_READY ) + ErrorExit( "\nDevice %s not ready\n", chDest + 4 ); + else + ErrorExit( "\nCannot access device %s (GLE=%d)\n", + chDest + 4, + GetLastError() ); + } + + fDiskType = DISKTYPE_UNKNOWN; + nElements = dwActual / sizeof( dgArray[ 0 ] ); + + for ( n = 0; n < nElements; n++ ) { + switch ( dgArray[ n ].MediaType ) { + case F5_1Pt2_512: + fDiskType = DISKTYPE_525; + break; + case F3_1Pt44_512: + if ( bFormatTargetDMF ) + fDiskType = DISKTYPE_DMF; + else + fDiskType = DISKTYPE_35; + break; + case F3_2Pt88_512: + if ( bFormatTargetDMF ) + ErrorExit( "\nCannot create DMF diskette in 2.88MB drive\n" ); + break; + } + } + + if ( fDiskType == DISKTYPE_UNKNOWN ) + ErrorExit( "Device %s does not support required media\n", + chDest + 4 ); + + bSuccess = DeviceIoControl( hDevice, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, + 0, + &dg, + sizeof( dg ), + &dwActual, + NULL ); + + if ( ! bSuccess ) { + + if (( dwGLE = GetLastError() ) == ERROR_NOT_READY ) + ErrorExit( "\nDevice %s not ready\n", chDest + 4 ); + else + ErrorExit( "\nCannot access device %s (GLE=%d)\n", + chDest + 4, + GetLastError() ); + } + + switch ( dg.MediaType ) { + case Unknown: + bFormatTarget = TRUE; + if ( fDiskType == DISKTYPE_DMF ) + bFormatTargetDMF = TRUE; + break; + case F5_1Pt2_512: + if ( fDiskType == DISKTYPE_DMF ) + ErrorExit( "\nCannot format 5.25\" media with DMF\n" ); + else if ( fDiskType != DISKTYPE_525 ) + ErrorExit( "\nIncompatible media in device %s\n", + chDest + 4 ); + break; + case F3_1Pt44_512: + if ( fDiskType == DISKTYPE_DMF ) { + bFormatTargetDMF = TRUE; + bFormatTarget = TRUE; + } + else if ( fDiskType == DISKTYPE_35 ) { + if ( dg.SectorsPerTrack != 18 ) + bFormatTarget = TRUE; // back to 1.44 from DMF + } + else { + ErrorExit( "\nIncompatible media in device %s\n", + chDest + 4 ); + } + break; + default: + ErrorMessage( "\nMedia in device %s contains a format other than that specified." + "\nWill attempt to re-format %s to specified format.\n", + chDest + 4, + chDest + 4 ); + bFormatTarget = TRUE; + } + + } + + wMaxRootEntries = (fDiskType == DISKTYPE_DMF) ? + MAX_ROOT_ENTRIES_DMF : + MAX_ROOT_ENTRIES_STD; + + wClusterSize = (fDiskType == DISKTYPE_DMF) ? + CLUSTER_SIZE_DMF : + CLUSTER_SIZE_STD; + } + + +void ParseSources( PPARAM pParamList ) { + + // + // All remaining pParamList entries should be source file + // specifiers. Each separate parameter should be expanded + // and sorted, then output in that order. + // + + CHAR chSourcePath[ MAX_PATH ]; + CHAR chSourceFileName[ MAX_PATH ]; + UINT nFiles, nSourceSpecifiers, nFilesThisSource; + LPSTR pszFileNameInSourcePath; + LPSTR pszTargetFileName; + PFILENODE pTargetDirForThisSource; + LPSTR pszPreviousTargetDir, p; + DWORD dwAttrib; + PPARAM pPrev, pParam; + BOOL bAbortAfterScan = FALSE; + + nFiles = 0; + nSourceSpecifiers = 0; + pszPreviousTargetDir = NULL; + pTargetDirForThisSource = NULL; + + pRootDir = NewFileNode(); + pRootDir->pszTargetFileName = DuplicateString( "" ); + pRootDir->dwWin32Attributes = FILE_ATTRIBUTE_DIRECTORY; + pRootDir->ftWin32TimeStamp = ftGlobalFileTime; + + if ( pParamList->pNext == NULL ) { + ErrorMessage( "No source parameters specified\n" ); + return; + } + + ErrorMessage( "Scanning source files..." ); + + for ( pPrev = pParamList, pParam = pParamList->pNext; + pParam != NULL; + pPrev = pParam, pParam = pParam->pNext ) { + + ++nSourceSpecifiers; + + if (( ! pParam->pszTargetDir ) || + ( ! *pParam->pszTargetDir )) { + ErrorExit( "\nASSERTION FAILURE: pParam->pszTargetDir\n" ); + } + + // + // If target dir is root (0x005C == "\"), don't add it. + // + // If target dir is same as previous target dir, no point + // in trying to add it to the list again because we can + // assume it's already there (merely an optimization). + // + + if ( pParam->pszTargetDir != pszPreviousTargetDir ) { + + if ( *(UNALIGNED WORD *)( pParam->pszTargetDir ) == 0x005C ) + pTargetDirForThisSource = pRootDir; + else + AddDirectoryToList( pParam->pszTargetDir, + pRootDir, + nSourceSpecifiers, + &pTargetDirForThisSource ); + + pszPreviousTargetDir = pParam->pszTargetDir; + } + + if (( pszTargetFileName = strchr( pParam->pszParam, '=' )) != NULL ) { + + // + // source\foo.txt=bar.txt + // + // foo.txt becomes bar.txt on target + // + + *pszTargetFileName++ = '\0'; + + if ( strchr( pszTargetFileName, '=' )) { + ErrorMessage( "\n%s: invalid target file name", + pParam->pszParam ); + bAbortAfterScan = TRUE; + } + + if ( *pszTargetFileName == '\\' ) { + pTargetDirForThisSource = pRootDir; + pszPreviousTargetDir = NULL; + do ++pszTargetFileName; + while ( *pszTargetFileName == '\\' ); + } + + while (( p = strchr( pszTargetFileName, '\\' )) != NULL ) { + + do *p++ = '\0'; + while ( *p == '\\' ); + + AddDirectoryToList( pszTargetFileName, + pTargetDirForThisSource, + nSourceSpecifiers, + &pTargetDirForThisSource ); + + pszPreviousTargetDir = NULL; + + pszTargetFileName = p; + } + + if ( *pszTargetFileName == '\0' ) + pszTargetFileName = NULL; + + } + + if ( GetFullPathName( pParam->pszParam, + MAX_PATH, + chSourcePath, + &pszFileNameInSourcePath )) { + + if ( pszFileNameInSourcePath ) { + + dwAttrib = GetFileAttributes( chSourcePath ); + + if (( dwAttrib != 0xFFFFFFFF ) && + ( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )) { + + strcat( chSourcePath, "\\" ); + strcpy( chSourceFileName, "*" ); + } + else { + strcpy( chSourceFileName, pszFileNameInSourcePath ); + *pszFileNameInSourcePath = '\0'; + } + } + else { + strcpy( chSourceFileName, "*" ); + } + + if (( pszTargetFileName != NULL ) && + (( strchr( chSourceFileName, '*' )) || + ( strchr( chSourceFileName, '?' )))) { + + ErrorMessage( "\n%s: cannot rename wildcard", + pParam->pszParam ); + bAbortAfterScan = TRUE; + } + + nFilesThisSource = ParseSourceParameter( pTargetDirForThisSource, + chSourcePath, + chSourceFileName, + pszTargetFileName, + pParam->bRecurse, + nSourceSpecifiers ); + + if ( nFilesThisSource == 0 ) { + ErrorMessage( "\n%s: file not found", + pParam->pszParam ); + bAbortAfterScan = TRUE; + } + + nFiles += nFilesThisSource; + + } + else { + ErrorMessage( "\n%s: invalid file specifier", + pParam->pszParam ); + bAbortAfterScan = TRUE; + } + } + + if ( bAbortAfterScan ) + ErrorExit( "\n" ); + + if ( nSourceSpecifiers > 0 ) { + if ((( ! bIncludeEmptyDirectories ) && ( TrimTheTree( pRootDir ))) + || ( ! pRootDir->pFirstFile )) { + + ErrorExit( "\nNo files to copy\n" ); + + } + } + + ErrorMessage( "done\n" ); + } + + +UINT ParseSourceParameter( PFILENODE pParentDir, + LPSTR pszSourcePath, + LPSTR pszSourceFileName, + LPSTR pszTargetFileName, + BOOL bRecursive, + UINT nSourceIndex ) { + + LPSTR pszNewPortionOfSourcePath; + LPSTR pszAllocatedSourcePath; + PFILENODE pThisDir; + WIN32_FIND_DATA fd; + HANDLE hFind; + UINT nFilesInThisDir = 0; + UINT nFilesInSubDirs = 0; + UINT nPathLength; + + // + // Put SourcePath in allocated memory so can reference + // through file node later. + // + // Optimization: would be better to search list of known + // source paths and reuse them rather than malloc'ing new + // buffer regardless. Oh well, memory is cheap, right? + // + + nPathLength = strlen( pszSourcePath ); + pszAllocatedSourcePath = MyAlloc( nPathLength + 1 ); + memcpy( pszAllocatedSourcePath, pszSourcePath, nPathLength + 1 ); + + // + // We're going to modify the memory at pszSourcePath, but we'll + // restore it before returning. Saves having MAX_PATH buffers + // on the stack for each recursion. We'll set a marker, + // pszNewPortionOfSourcePath, to the end of the original + // string, and we'll only be modifying beyond that, so all + // we have to do before returning is restore the null at + // this address. + // + + pszNewPortionOfSourcePath = pszSourcePath + nPathLength; + + + // + // Now we start on find loop for files in SourcePath that + // match pszFileName pattern. Note we're extending the + // pszSourcePath string at the NewPortionOf... location. + // + + strcpy( pszNewPortionOfSourcePath, pszSourceFileName ); + + hFind = FindFirstFile( pszSourcePath, &fd ); + + if ( hFind != INVALID_HANDLE_VALUE ) { + + do { + + // + // ignore directories and hidden files. If we're recursive + // descent, we'll pick up directories on next find pass + // + + if (( ! ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )) && + (( ! ( fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )) || + ( bCopyAttributes ))) { + + AddFileToList( &fd, + pParentDir, + pszAllocatedSourcePath, + pszTargetFileName, + nSourceIndex, + NULL ); + + ++nFilesInThisDir; + + } + } + while ( FindNextFile( hFind, &fd )); + + FindClose( hFind ); + + if ( nFilesInThisDir == 0 ) { + + // + // Well, we can at least free up the alloc'd path + // since nobody is pointing at it. + // + + MyFree( pszAllocatedSourcePath ); + + } + } + + if ( bRecursive ) { + + // + // Now we go back through this directory looking for directories + // so we use "*" as the file specifier. + // + + strcpy( pszNewPortionOfSourcePath, "*" ); + + hFind = FindFirstFile( pszSourcePath, &fd ); + + if ( hFind != INVALID_HANDLE_VALUE ) { + + do { + + // + // we're only looking for directories here, but not + // "." and ".." since we'll handle those separately. + // + + if (( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && + ( ! ( IsFileNameDotOrDotDot( fd.cFileName ))) && + (( ! ( fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )) || + ( bCopyAttributes ))) { + + // + // Note that we're re-using the file pattern buffer here + // even though it was the buffer supplied to the + // FindFirstFile function and we're still doing + // FindNextFile calls -- the API spec doesn't say that + // the FindFirstFile pattern buffer must remain intact + // through subsequent FindNextFile calls. + // + + AddFileToList( &fd, + pParentDir, + NULL, + NULL, + nSourceIndex, + &pThisDir ); + + strcpy( pszNewPortionOfSourcePath, fd.cFileName ); + strcat( pszNewPortionOfSourcePath, "\\" ); + + nFilesInSubDirs = ParseSourceParameter( pThisDir, + pszSourcePath, + pszSourceFileName, + pszTargetFileName, + bRecursive, + nSourceIndex ); + + nFilesInThisDir += nFilesInSubDirs; + + } + } + + while ( FindNextFile( hFind, &fd )); + + FindClose( hFind ); + + } + } + + // + // Now restore the source strings to their original + // contents by replacing the NULLs at their original + // locations (we didn't modify anything preceding the + // NULLS). + // + + *pszNewPortionOfSourcePath = '\0'; + + return nFilesInThisDir; + + } + + +BOOL AddFileToList( WIN32_FIND_DATA *fd, + PFILENODE pParentDir, + LPSTR pszSourcePath, + LPSTR pszTargetFileName, + UINT nSourceIndex, + PFILENODE *pThisNode ) { + + PFILENODE pPrev, pNext, pNew; + CHAR chSourceFileName[ MAX_PATH ]; + CHAR chTargetFileName[ MAX_PATH ]; + LPSTR pExtension; + UINT nLength; + INT iCompare; + + if ( *fd->cAlternateFileName ) + strcpy( chSourceFileName, fd->cAlternateFileName ); + else + strcpy( chSourceFileName, fd->cFileName ); + + if ( pszTargetFileName ) + strcpy( chTargetFileName, pszTargetFileName ); + else + strcpy( chTargetFileName, chSourceFileName ); + + _strupr( chTargetFileName ); + pExtension = strchr( chTargetFileName, '.' ); + + if ((( pExtension == NULL ) && ( strlen( chTargetFileName ) > 8 )) || + (( pExtension ) && + (( pExtension - chTargetFileName > 8 ) || + ( strlen( pExtension + 1 ) > 3 )))) { + + ErrorExit( "\n%s: invalid filename\n", chTargetFileName ); + } + + for ( pPrev = NULL, pNext = pParentDir->pFirstFile; + pNext != NULL; + pPrev = pNext, pNext = pNext->pNextFile ) { + + iCompare = strcmp( pNext->pszTargetFileName, chTargetFileName ); + + if ( ! iCompare ) { + + // + // This file target name already exists. If the new and + // existing entries are both directories, ignore the new + // entry and just return the existing one. If the new + // and existing entries aren't both directories, compare + // attributes, filesize, and timestamps to verify that it + // is the same file -- if so, ignore new entry and return + // existing one. + // + + if (( ! ( pNext->dwWin32Attributes & + fd->dwFileAttributes & + FILE_ATTRIBUTE_DIRECTORY )) && + (( pNext->dwWin32Attributes != fd->dwFileAttributes ) || + ( pNext->dwFileSize != fd->nFileSizeLow ) || + ( pNext->ftWin32TimeStamp.dwLowDateTime != fd->ftLastWriteTime.dwLowDateTime ))) { + + ErrorExit( "\n%s: different file already specified with same name\n", + chSourceFileName ); + } + + if ( pThisNode ) + *pThisNode = pNext; + + return FALSE; + + } + + else if (( pNext->nSourceIndex >= nSourceIndex ) && + ( iCompare > 0 )) { + break; + } + + } + + pNew = NewFileNode(); + pNew->pszTargetFileName = DuplicateString( chTargetFileName ); + pNew->pszSourceFileName = DuplicateString( chSourceFileName ); + pNew->pszSourcePath = pszSourcePath; + pNew->dwFileSize = fd->nFileSizeLow; + pNew->dwWin32Attributes = fd->dwFileAttributes; + pNew->ftWin32TimeStamp = fd->ftLastWriteTime; + pNew->nSourceIndex = nSourceIndex; + pNew->pParentDir = pParentDir; + pNew->pNextFile = pNext; + + if ( pPrev ) + pPrev->pNextFile = pNew; + else + pParentDir->pFirstFile = pNew; + + if ( pThisNode ) + *pThisNode = pNew; + + nLength = ( pszSourcePath ? strlen( pszSourcePath ) : 0 ) + + strlen( chSourceFileName ); + + if ( nLength > nMaxSourceLength ) + nMaxSourceLength = nLength; + + return TRUE; + + } + + +void ParseOptions( PPARAM pParamList ) { + + PPARAM pPrev, pParam; + LPSTR pCurrentTargetDir; + BOOL bNextPatternIsRecursive; + char *p; + + // + // The option arguments may appear anywhere in the command line + // (beginning, end, middle, mixed), so for each option we encounter, + // we'll remove it after processing it. Then, the other Parse + // routines can loop through the whole parameter list again. + // + + pCurrentTargetDir = "\\"; + bNextPatternIsRecursive = FALSE; + + pPrev = pParamList; + pParam = pParamList->pNext; + + while ( pParam ) { + + p = pParam->pszParam; + + if (( p ) && (( *p == '-' ) || ( *p == '/' ))) { // process flags + + ++p; + + switch( tolower( *p )) { + + case '?': // help (usage) + case 'h': + Usage(); + break; + + case 't': // timestamp + + if ( *( ++p )) { + ParseTimeStamp( p ); + } + else if ( pParam->pNext ) { + ParseTimeStamp( pParam->pNext->pszParam ); + RemoveParam( pParam->pNext, pParam ); + } + break; + + case 'l': // volume label + + if ( *( ++p )) { + strncpy( chLabel, p, 11 ); + } + else if ( pParam->pNext ) { + strncpy( chLabel, pParam->pNext->pszParam, 11 ); + RemoveParam( pParam->pNext, pParam ); + } + _strupr( chLabel ); + break; + + case 'g': // tagfile + + if ( *( ++p )) { + strncpy( chTagFile, p, 11 ); + } + else if ( pParam->pNext ) { + strncpy( chTagFile, pParam->pNext->pszParam, 11 ); + RemoveParam( pParam->pNext, pParam ); + } + _strupr( chTagFile ); + break; + + case 'd': // double write + bDoubleWrite = TRUE; + break; + + case 'f': // format first + bFormatTarget = TRUE; + break; + + case 'm': // format 3.5" HD floppy as 1.7MB DMF + bFormatTargetDMF = TRUE; + fDiskType = DISKTYPE_DMF; + break; + + case 'w': // mark DMF target writable + bWritableDMF = TRUE; + break; + + case 'v': // verify target + bVerifyTarget = TRUE; + break; + + case 'a': // copy file attributes + bCopyAttributes = TRUE; + break; + + case 'z': // zero unused sectors + bZeroSlack = TRUE; + break; + + case '3': // target is 3.5" HD + fDiskType = DISKTYPE_35; + break; + + case '5': // target is 5.25" HD + fDiskType = DISKTYPE_525; + break; + + case '8': // target is 3.5" HD DMF (1.7MB) + fDiskType = DISKTYPE_DMF; + bFormatTargetDMF = TRUE; + break; + + case 'b': // boot sector specifier + + if ( *( ++p )) { + strcpy( chBootSpecifier, p ); + } + else if ( pParam->pNext ) { + strcpy( chBootSpecifier, pParam->pNext->pszParam ); + RemoveParam( pParam->pNext, pParam ); + } + break; + + case 's': // include subdirs + bNextPatternIsRecursive = TRUE; + if ( *( ++p )) { + pParam->pszParam = p; // file pattern here + continue; // parse this pParam again + } + break; + + case 'e': // include empty subs + + bIncludeEmptyDirectories = TRUE; + bNextPatternIsRecursive = TRUE; + + // + // note that /e may be specified with or without /s, + // so for either of them, mark recursive for following + // source specifier. + // + + if ( *( ++p )) { + pParam->pszParam = p; // file pattern here + continue; // parse this pParam again + } + break; + + case 'x': // target subdirectory + + if ( *( ++p )) { + pCurrentTargetDir = p; + } + else if ( pParam->pNext ) { + pCurrentTargetDir = pParam->pNext->pszParam; + RemoveParam( pParam->pNext, pParam ); + } + break; + + default: // say what?!!! + ErrorExit( "\ninvalid option \"-%c\"\n", *p ); + + } + + pParam = RemoveParam( pParam, pPrev ); + + } + + else { + + // + // Assume this is a file pattern argument. + // + + pParam->pszTargetDir = pCurrentTargetDir; + pParam->bRecurse = bNextPatternIsRecursive; + + bNextPatternIsRecursive = FALSE; + + pPrev = pParam; + pParam = pParam->pNext; + + } + } + } + + +void ParseTimeStamp( LPSTR pArg ) { + + // + // Expected form: month/day/year/hour/minute/second + // with any non-digit character being a delimiter. + // + + UINT i, arg[ 6 ] = { 0, 0, 0, 0, 0, 0 }; + char *p = pArg; + + if ( p ) { + + for ( i = 0; i < 6; i++ ) { + + while ( IsDigit( *p )) + arg[ i ] = ( arg[ i ] * 10 ) + ( *p++ - '0' ); + + while (( *p ) && ( ! IsDigit( *p ))) ++p; + + } + + if ( arg[ 2 ] < 50 ) // if year is 00 to 49, assume 2000 to 2049 + arg[ 2 ] += 2000; + + if ( arg[ 2 ] < 100 ) // if year is 50 to 99, assume 1950 to 1999 + arg[ 2 ] += 1900; + + if (( arg[ 0 ] < 1 ) || ( arg[ 0 ] > 12 ) || // month + ( arg[ 1 ] < 1 ) || ( arg[ 1 ] > 31 ) || // day + ( arg[ 2 ] < 1980 ) || ( arg[ 2 ] > 2107 ) || // year + ( arg[ 3 ] > 23 ) || // hour + ( arg[ 4 ] > 59 ) || // minute + ( arg[ 5 ] > 59 )) { // second + + ErrorExit( + "\nInvalid time specified: %02d/%02d/%04d,%02d:%02d:%02d\n", + arg[ 0 ], + arg[ 1 ], + arg[ 2 ], + arg[ 3 ], + arg[ 4 ], + arg[ 5 ] + ); + + } + + wDosDate = ((( arg[ 2 ] - 1980 ) << 9 ) & 0xFE00 ) | // years since 1980 (0..127) + (( arg[ 0 ] << 5 ) & 0x01E0 ) | // month (1..12) + ( arg[ 1 ] & 0x001F ); // day (1..31) + + wDosTime = (( arg[ 3 ] << 11 ) & 0xF800 ) | // hour (0..23) + (( arg[ 4 ] << 5 ) & 0x07E0 ) | // minute (0..59) + (( arg[ 5 ] >> 1 ) & 0x001F ); // seconds/2 (0..29) + } + + bUseGlobalTime = TRUE; + + } + +void Usage( void ) { + + ErrorExit( + +"\n" +"fcopy [options] [@response] [[-s] source[=rename] ...] destination\n" +"\n" +" Options: -l -g -t -a -b -w -e -z -d -f -m -v -3 -5 -8\n" +"\n" +" Multiple sources can be specified (order preserved, alphabetically sorted\n" +" within wildcard specifiers). Destination may be floppy drive (A:, B:), or\n" +" the name of a floppy image file to create. A response file (@filename) can\n" +" be used to specify options rather than lengthy command line. Environment\n" +" variables are expanded in the response file same as command line. Rename\n" +" specifier can be full or partial target path and/or filename.\n" +"\n" +" Common options:\n" +"\n" +" -l volume label (e.g. -l DISK1)\n" +" -g create zero-length tagfile in root directory (e.g. -g DISK1)\n" +" -t time stamp for all files month/day/year/hour/minute/second\n" +" (no spaces, e.g. -t 12/31/91,15:01:00)\n" +" -a copy file attributes vs. ignoring hidden files/directories\n" +" -b boot sector specifier, can be filename or one of hardcoded:\n" +" \"DOS\" (MS-DOS 5.0, default), \"NT31SETUP\", \"NT35SETUP\"\n" +" -w (DMF only) mark target as writable versus write-protected\n" +" -s include subdirectories for next source specifier\n" +" -e include empty subdirectories as well (for all sources)\n" +" -x specify a subdirectory on the target for subsequent files\n" +" (e.g. put subsequent files in i386 directory: -x i386)\n" +"\n" +" Options when destination is a physical floppy disk:\n" +" -z zero-fill unused sectors vs. only writing necessary sectors\n" +" -d double-write mode writes each sector twice\n" +" -f format target disk prior to writing data (standard format)\n" +" -m format target 3.5\" HD floppy with 1.7MB DMF format\n" +" -v verify (read and compare) destination after writing\n" +"\n" +" Options when destination is a floppy image file:\n" +" -3 specify target is 3.5\" HD 1.44MB floppy (default)\n" +" -5 specify target is 5.25\" HD 1.2MB floppy\n" +" -8 specify target is 3.5\" HD 1.7MB DMF floppy\n" +"\n" +" For Microsoft internal use only -- DMF is proprietary and protected.\n" +"\n" + ); + } + + +BOOL IsDigit( char ch ) { + + if (( ch >= '0' ) && ( ch <= '9' )) + return TRUE; + else + return FALSE; + } + + +void ErrorMessage( const char *szFormat, ... ) { + va_list vaArgs; + + va_start( vaArgs, szFormat ); + vfprintf( stdout, szFormat, vaArgs ); + va_end( vaArgs ); + } + + +void ErrorExit( const char *szFormat, ... ) { + va_list vaArgs; + + va_start( vaArgs, szFormat ); + vfprintf( stdout, szFormat, vaArgs ); + va_end( vaArgs ); + + exit( 1 ); // can't call ExitProcess since C-runtime not flushed + } + + +UINT Min( UINT a, UINT b ) { + if ( a < b ) + return a; + else + return b; + } + + +void ReWriteDestination( LPSTR pszMessage ) { + + PUCHAR pData = pWholeDiskBuffer; + UINT nBytes, nActual; + DWORD dwGLE; + + ErrorMessage( pszMessage ); + + OverlappedVerifyComplete( hTarget ); + + nBytes = pWholeDiskCurrentLocation - pWholeDiskBuffer; + +#ifdef THE_OLD_WAY + + UINT i, nClusters, nBuffers; + + OverlappedSetFilePointer( hTarget, 0 ); + SetFilePointer( hTarget, 0, NULL, FILE_BEGIN ); + + nClusters = ROUNDUP2( nBytes, wClusterSize ) / wClusterSize; + nBuffers = nClusters / ( BUFFER_SIZE / wClusterSize ); + + for ( i = 0; i < nBuffers; i++ ) { + OverlappedWriteFile( hTarget, pData, BUFFER_SIZE ); + pData += BUFFER_SIZE; + } + + nClusters -= ( nBuffers * ( BUFFER_SIZE / wClusterSize )); + + if ( nClusters ) + OverlappedWriteFile( hTarget, pData, wClusterSize * nClusters ); + +#endif // THE_OLD_WAY + + olControl.Offset = 0; + olControl.OffsetHigh = 0; + + if ( WriteFile( hTarget, + pWholeDiskBuffer, + nBytes, + &nActual, + &olControl )) { + + // + // completed synchronously + // + + if ( nActual != nBytes ) + ErrorExit( "\nAssert: nActual != nBytes\n" ); + + } + + else if (( dwGLE = GetLastError() ) == ERROR_IO_PENDING ) { + + // + // asynchronous -- wait for completion + // + + if ( GetOverlappedResult( hTarget, + &olControl, + &nActual, + TRUE )) { + + // + // completed, no error + // + + if ( nActual != nBytes ) + ErrorExit( "\nAssert: nActual != nBytes\n" ); + + } + + else { + + // + // asynchronous error + // + + ErrorExit( "\nError writing %s %s (GLE=%d)\n", + bDestIsDevice ? "device" : "file", + bDestIsDevice ? chDest + 4 : chDest, + GetLastError() ); + } + } + + else { + + // + // error + // + + ErrorExit( "\nError writing %s %s (GLE=%d)\n", + bDestIsDevice ? "device" : "file", + bDestIsDevice ? chDest + 4 : chDest, + dwGLE ); + } + + ErrorMessage( "done\n" ); + + } + + +void FormatTarget( void ) { + + DWORD dwGLE; + BOOL bSuccess; + FORMAT_PARAMETERS fp; + + if ( fDiskType == DISKTYPE_DMF ) { + ErrorMessage( "Formatting (DMF)..." ); + } else { + ErrorMessage( "Formatting..." ); + } + +#ifdef DONTCOMPILE // now leaving hDevice open as global + hDevice = CreateFile( chDest, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); + + if ( hDevice == INVALID_HANDLE_VALUE ) + ErrorExit( "\nCould not open device %s (GLE=%d)\n", + chDest + 4, + GetLastError() ); +#endif + + if ( fDiskType == DISKTYPE_DMF ) { + + bSuccess = DmfFormatTracks( hDevice, &dwGLE ); + + } else if ( fDiskType == DISKTYPE_525 ) { + fp.MediaType = F5_1Pt2_512; + } else if ( fDiskType == DISKTYPE_35 ) { + fp.MediaType = F3_1Pt44_512; + } else { + ErrorExit( "\nBad media type\n" ); + } + + if ( fDiskType != DISKTYPE_DMF) { + + fp.StartCylinderNumber = 0; + fp.EndCylinderNumber = 79; + fp.StartHeadNumber = 0; + fp.EndHeadNumber = 1; + + bSuccess = DeviceIoControl( hDevice, + IOCTL_DISK_FORMAT_TRACKS, + &fp, + sizeof( fp ), + NULL, + 0, + &dwGLE, + NULL ); + + dwGLE = GetLastError(); + } + +#ifdef DONTCOMPILE // now leaving hDevice open as global + CloseHandle( hDevice ); +#endif + + if ( ! bSuccess ) { + + if ( dwGLE == ERROR_NOT_READY ) + ErrorExit( "\nDevice %s not ready\n", chDest + 4 ); + + ErrorExit( "\nCould not format disk in drive %s (GLE=%d)\n", + chDest + 4, + GetLastError() ); + } + + ErrorMessage( "done\n" ); + } + + +void VerifyDestination( void ) { + + UINT i, nBytes, nClusters, nBuffers; + PUCHAR pData = pWholeDiskBuffer; + DWORD dwBytes, dwOffset, dwBufferOffset; + + ErrorMessage( "Verifying..." ); + + OverlappedSetFilePointer( hTarget, 0 ); + + nBytes = pWholeDiskCurrentLocation - pWholeDiskBuffer; + + nClusters = ROUNDUP2( nBytes, wClusterSize ) / wClusterSize; + nBuffers = nClusters / ( BUFFER_SIZE / wClusterSize ); + dwOffset = 0; + + for ( i = 0; i < nBuffers; i++ ) { + + OverlappedReadFile( hTarget, pAlignedBuffer, BUFFER_SIZE, &dwBytes ); + OverlappedVerifyComplete( hTarget ); + + dwBufferOffset = CompareAligned( pAlignedBuffer, pData, BUFFER_SIZE ); + if ( dwBufferOffset != BUFFER_SIZE ) + ErrorExit( "\nVerification error mismatch (offset=0x%08X)\n", + dwOffset + dwBufferOffset ); + + dwOffset += BUFFER_SIZE; + pData += BUFFER_SIZE; + } + + nClusters -= ( nBuffers * ( BUFFER_SIZE / wClusterSize )); + + if ( nClusters ) { + + OverlappedReadFile( hTarget, pAlignedBuffer, wClusterSize * nClusters, &dwBytes ); + OverlappedVerifyComplete( hTarget ); + + dwBytes = wClusterSize * nClusters; // bytes remaining to compare + + dwBufferOffset = CompareAligned( pAlignedBuffer, pData, dwBytes ); + if ( dwBufferOffset != dwBytes ) + ErrorExit( "\nVerification compare error (offset=0x%08X)\n", + dwOffset + dwBufferOffset ); + + } + + ErrorMessage( "done\n" ); + + } + + +void GetBootSector( void ) { + + // + // chBootSpecifier is either a filename or one of several possible + // hardcoded options which can be added here. Note that if no + // chBootSpecifier is specified, we default to DOS boot sector. + // + + if (( *chBootSpecifier == '\0' ) || + ( _stricmp( chBootSpecifier, "DOS" ) == 0 ) || + ( _stricmp( chBootSpecifier, "DOS5" ) == 0 )) { + CopyMemory( BootSector, Dos5BootSector, SECTOR_SIZE ); + } + else if ( _stricmp( chBootSpecifier, "NT31SETUP" ) == 0 ) { + CopyMemory( BootSector, Nt31SetupBootSector, SECTOR_SIZE ); + } + else if ( _stricmp( chBootSpecifier, "NT35SETUP" ) == 0 ) { + CopyMemory( BootSector, Nt35SetupBootSector, SECTOR_SIZE ); + } + else { + + HANDLE hFile; + DWORD dwBytes; + + hFile = CreateFile( chBootSpecifier, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL ); + + if ( hFile == INVALID_HANDLE_VALUE ) + ErrorExit( "\nCannot open boot sector file %s (GLE=%d)\n", + chBootSpecifier, + GetLastError() ); + + if ( ! ReadFile( hFile, BootSector, SECTOR_SIZE, &dwBytes, NULL )) + ErrorExit( "\nCannot read boot sector file %s (GLE=%d)\n", + chBootSpecifier, + GetLastError() ); + + if ( dwBytes != SECTOR_SIZE ) { + ErrorExit( "\nBoot sector file %s not %d bytes\n", + chBootSpecifier, SECTOR_SIZE ); + } + + CloseHandle( hFile ); + + } + } + + +DWORD CompareAligned( PVOID pBuffer1, PVOID pBuffer2, DWORD dwLength ) { + + // + // Buffers are expected to be DWORD aligned. The return + // value is the offset of the miscompare, or dwLength if + // buffers are equivalent. The offset returned may be a + // non-DWORD aligned number such as 6 if that is the byte + // offset of the first miscompare. + // + + DWORD dwOffset, dwAlignedLength; + union { + PUCHAR pch; + PDWORD pdw; + } p1, p2; + + p1.pdw = pBuffer1; + p2.pdw = pBuffer2; + + dwAlignedLength = dwLength & ( ~ ( sizeof( DWORD ) - 1 )); + + for ( dwOffset = 0; + dwOffset < dwAlignedLength; + dwOffset += sizeof( DWORD ), p1.pdw++, p2.pdw++ ) { + + if ( *p1.pdw != *p2.pdw ) + break; + + } + + while (( dwOffset < dwLength ) && ( *p1.pch++ == *p2.pch++ )) + ++dwOffset; + + return dwOffset; + } + + +PVOID MyAllocZ( UINT nBytes ) { + PVOID pAlloc = MyAlloc( nBytes ); + ZeroMemory( pAlloc, nBytes ); + return pAlloc; + } + + +PVOID MyAlloc( UINT nBytes ) { + + // + // assume single-threaded access for HEAP_NO_SERIALIZE + // + + PVOID pAlloc = HeapAlloc( hProcessHeap, HEAP_NO_SERIALIZE, nBytes ); + + if ( pAlloc == NULL ) { + ErrorExit( "\nOut of memory\n" ); + } + + return pAlloc; + } + + +VOID MyFree( PVOID pAlloc ) { + HeapFree( hProcessHeap, HEAP_NO_SERIALIZE, pAlloc ); + } + + +BOOL IsFileNameDotOrDotDot( LPCSTR pszFileName ) { + + DWORD dwFirstFourBytes = *(UNALIGNED DWORD *)pszFileName; + + // + // assume four bytes are readable even if string is only one or two bytes + // but we won't assume it's dword-aligned. A dot character is hex 2E, + // and we have to verify it's followed by NULL to qualify. + // + + if ((( dwFirstFourBytes & 0x0000FFFF ) == 0x0000002E ) || + (( dwFirstFourBytes & 0x00FFFFFF ) == 0x00002E2E )) + + return TRUE; + + return FALSE; + + } + + +PFILENODE NewFileNode( void ) { + return MyAllocZ( sizeof( FILENODE )); + } + + +LPSTR DuplicateString( LPCSTR pString ) { + UINT nLength = strlen( pString ) + 1; + LPSTR pAlloc = MyAlloc( nLength ); + CopyMemory( pAlloc, pString, nLength ); + return pAlloc; + } + + +void AssignDirectoryClusters( PFILENODE pDir ) { + + PFILENODE pNode; + UINT nCount; + + if ( pDir->pParentDir == NULL ) { + + // + // This is the root directory -- don't add . and .. + // counts, and don't allocate clusters. + // + + for ( pNode = pDir->pFirstFile, + nCount = (( *chLabel ) ? 1 : 0 ) + (( *chTagFile ) ? 1 : 0 ); + pNode; + pNode = pNode->pNextFile, + nCount++ ); + + if ( nCount > wMaxRootEntries ) { + ErrorExit( "\nMore than %d files in root directory\n", + wMaxRootEntries ); + } + + pDir->dwFileSize = wMaxRootEntries * 32; // 32 bytes/directory entry + + } + + else { + + for ( pNode = pDir->pFirstFile, + nCount = 2; + pNode; + pNode = pNode->pNextFile, + nCount++ ); + + pDir->dwFileSize = nCount * sizeof( DIRENTRY ); + pDir->dwStartingCluster = AllocateFat( pDir->dwFileSize ); + + } + + for ( pNode = pDir->pFirstFile; + pNode; + pNode = pNode->pNextFile ) { + + if ( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) { + AssignDirectoryClusters( pNode ); + } + } + } + + +void AssignFileClusters( PFILENODE pDir ) { + + PFILENODE pNode; + + for ( pNode = pDir->pFirstFile; pNode; pNode = pNode->pNextFile ) { + if ( ! ( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY )) { + pNode->dwStartingCluster = AllocateFat( pNode->dwFileSize ); + } + } + + for ( pNode = pDir->pFirstFile; pNode; pNode = pNode->pNextFile ) { + + if ( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) { + AssignFileClusters( pNode ); + } + } + } + + +DWORD AllocateFat( DWORD dwFileSize ) { + + DWORD dwStartingCluster, dwNextCluster; + UINT nClusters; + + if ( dwFileSize == 0 ) + return 0; + + nClusters = ROUNDUP2( dwFileSize, wClusterSize ) / wClusterSize; + dwStartingCluster = dwNextCluster = nNextAvailableCluster; + + if (( nNextAvailableCluster += nClusters ) > nMaxFatEntries ) { + ErrorExit( "\nFiles won't fit\n" ); + } + + for ( ; --nClusters; dwNextCluster++ ) { + Fat[ dwNextCluster ] = (WORD)( dwNextCluster + 1 ); + } + + Fat[ dwNextCluster ] = 0xFFF; // 12-bit FAT EOF + + return dwStartingCluster; + + } + + +UINT CreateAndWriteDirectories( PFILENODE pDir ) { + + PDIRENTRY pDirBuffer, pNextDirEntry; + DWORD dwFileAllocationSize, dwBytes; + PFILENODE pNode; + UINT nClusters; + PCHAR pWorkingBuffer; + + dwFileAllocationSize = ROUNDUP2( pDir->dwFileSize, (pDir == pRootDir) ? SECTOR_SIZE : wClusterSize ); + + pNextDirEntry = pDirBuffer = MyAllocZ( dwFileAllocationSize ); + + if ( pDir == pRootDir ) { + nClusters = 0; // root directory space not allocated from FAT + if ( *chLabel ) { + CreateVolumeLabelEntry( pNextDirEntry++ ); + } + if ( *chTagFile ) { + CreateTagFileEntry( pNextDirEntry++ ); + } + } + else { + nClusters = dwFileAllocationSize / wClusterSize; + + CreateDirEntry( pNextDirEntry++, + ".", + FILE_ATTRIBUTE_DIRECTORY, + pDir->ftWin32TimeStamp, + pDir->dwStartingCluster, + 0 ); + + CreateDirEntry( pNextDirEntry++, + "..", + FILE_ATTRIBUTE_DIRECTORY, + pDir->ftWin32TimeStamp, + pDir->pParentDir->dwStartingCluster, + 0 ); + } + + for ( pNode = pDir->pFirstFile; pNode; pNode = pNode->pNextFile ) { + CreateDirEntry( pNextDirEntry++, + pNode->pszTargetFileName, + pNode->dwWin32Attributes, + pNode->ftWin32TimeStamp, + pNode->dwStartingCluster, + pNode->dwFileSize ); + } + + pWorkingBuffer = (PCHAR)pDirBuffer; + + while ( dwFileAllocationSize ) { + dwBytes = Min( dwFileAllocationSize, BUFFER_SIZE ); + OverlappedWriteFile( hTarget, pWorkingBuffer, dwBytes ); + pWorkingBuffer += dwBytes; + dwFileAllocationSize -= dwBytes; + } + + MyFree( pDirBuffer ); + + for ( pNode = pDir->pFirstFile; pNode; pNode = pNode->pNextFile ) { + if ( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) { + nClusters += CreateAndWriteDirectories( pNode ); + } + } + + return nClusters; + } + + +void PadCopy( LPSTR pDest, LPCSTR pSource, UINT nLength ) { + + if ( pSource ) + for ( ; nLength && *pSource; nLength-- ) + *pDest++ = *pSource++; + + for ( ; nLength; nLength-- ) + *pDest++ = ' '; + + } + + +void CreateDirEntry( PDIRENTRY pDirEntry, + LPSTR pszFileName, + DWORD dwWin32Attributes, + FILETIME ftWin32TimeStamp, + DWORD dwStartingCluster, + DWORD dwFileSize ) { + + CHAR chNameBuffer[ MAX_PATH ]; + FILETIME ftLocalFileTime; + PCHAR pExtension; + BYTE bDosAttribute; + + strcpy( chNameBuffer, pszFileName ); + pExtension = strchr( chNameBuffer, '.' ); + if ( pExtension ) { + if ( pExtension == chNameBuffer ) + pExtension = NULL; // . or .. so leave alone + else + *pExtension++ = '\0'; + } + PadCopy( pDirEntry->BaseName, chNameBuffer, 8 ); + PadCopy( pDirEntry->Extension, pExtension, 3 ); + + bDosAttribute = DOS_ATTRIBUTE_NORMAL; + + if ( dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) + bDosAttribute |= DOS_ATTRIBUTE_DIRECTORY; + + if ( bCopyAttributes ) { + if ( dwWin32Attributes & FILE_ATTRIBUTE_READONLY ) + bDosAttribute |= DOS_ATTRIBUTE_READONLY; + if ( dwWin32Attributes & FILE_ATTRIBUTE_HIDDEN ) + bDosAttribute |= DOS_ATTRIBUTE_HIDDEN; + if ( dwWin32Attributes & FILE_ATTRIBUTE_SYSTEM ) + bDosAttribute |= DOS_ATTRIBUTE_SYSTEM; + if ( dwWin32Attributes & FILE_ATTRIBUTE_ARCHIVE ) + bDosAttribute |= DOS_ATTRIBUTE_ARCHIVE; + } + + pDirEntry->Attribute = bDosAttribute; + + if ( bUseGlobalTime ) { + pDirEntry->TimeStamp = wDosTime; + pDirEntry->DateStamp = wDosDate; + } + else { + FileTimeToLocalFileTime( &ftWin32TimeStamp, + &ftLocalFileTime ); + FileTimeToDosDateTime( &ftLocalFileTime, + &pDirEntry->DateStamp, + &pDirEntry->TimeStamp ); + } + +#ifdef NEW_NT_EXTENDED_FAT_DIRECTORY_ENTRY_INTERPRETATION + pDirEntry->Extended.NtByte = 0; + pDirEntry->Extended.CreationTimeMilliSeconds = 0; + pDirEntry->Extended.CreationTime = pDirEntry->TimeStamp; + pDirEntry->Extended.CreationDate = pDirEntry->DateStamp; + pDirEntry->Extended.LastAccessDate = pDirEntry->DateStamp; + pDirEntry->Extended.ExtendedAttributes = 0; +#else + memset( pDirEntry->Reserved, 0, sizeof( pDirEntry->Reserved )); +#endif + + pDirEntry->Cluster = (WORD)dwStartingCluster; + + if ( dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) + pDirEntry->FileSize = 0; + else + pDirEntry->FileSize = dwFileSize; + + } + + +void CreateVolumeLabelEntry( PDIRENTRY pDirEntry ) { + + PadCopy( pDirEntry->BaseName, chLabel, 11 ); + pDirEntry->Attribute = DOS_ATTRIBUTE_VOLUME; + pDirEntry->TimeStamp = wDosTime; + pDirEntry->DateStamp = wDosDate; + + } + + +void CreateTagFileEntry( PDIRENTRY pDirEntry ) { + + CHAR chNameBuffer[ MAX_PATH ]; + PCHAR pExtension; + + strcpy( chNameBuffer, chTagFile ); + pExtension = strchr( chNameBuffer, '.' ); + if ( pExtension ) + *pExtension++ = '\0'; + PadCopy( pDirEntry->BaseName, chNameBuffer, 8 ); + PadCopy( pDirEntry->Extension, pExtension, 3 ); + pDirEntry->Attribute = DOS_ATTRIBUTE_NORMAL; + pDirEntry->TimeStamp = wDosTime; + pDirEntry->DateStamp = wDosDate; + + } + + +UINT WalkListAndWriteFiles( PFILENODE pDir ) { + + PFILENODE pNode; + UINT nClusters; + + nClusters = 0; + + for ( pNode = pDir->pFirstFile; pNode; pNode = pNode->pNextFile ) { + if ( ! ( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY )) { + nClusters += OpenAndCopySourceFile( pNode ); + } + } + + for ( pNode = pDir->pFirstFile; pNode; pNode = pNode->pNextFile ) { + if ( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) { + nClusters += WalkListAndWriteFiles( pNode ); + } + } + + return nClusters; + + } + + +void BuildFullTargetName( PFILENODE pNode, LPSTR pBuffer ) { + + if ( pNode == pRootDir ) { + + if ( bDestIsDevice ) + strcpy( pBuffer, chDest + 4 ); + else + strcpy( pBuffer, chDest ); + } + + else { + + BuildFullTargetName( pNode->pParentDir, pBuffer ); + + if ( pNode->pszTargetFileName ) { + strcat( pBuffer, "\\" ); + strcat( pBuffer, pNode->pszTargetFileName ); + } + } + } + + +UINT OpenAndCopySourceFile( PFILENODE pNode ) { + + CHAR chFullSourceName[ MAX_PATH ] = ""; + CHAR chFullTargetName[ MAX_PATH ] = ""; + HANDLE hSource; + UINT nClusters; + + if ( pNode->pszSourcePath ) + strcpy( chFullSourceName, pNode->pszSourcePath ); + if ( pNode->pszSourceFileName ) + strcat( chFullSourceName, pNode->pszSourceFileName ); + + BuildFullTargetName( pNode, chFullTargetName ); + + _strlwr( chFullSourceName ); + _strlwr( chFullTargetName ); + + ErrorMessage( "%-*s -> %s ", nMaxSourceLength, chFullSourceName, chFullTargetName ); + + if ( pNode->dwFileSize == 0 ) { + ErrorMessage( "\n" ); + return 0; + } + + hSource = CreateFile( chFullSourceName, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL ); + + if ( hSource == INVALID_HANDLE_VALUE ) + ErrorExit( "\nError opening file (GLE=%d)\n", GetLastError() ); + + nClusters = CopyFileContents( hTarget, + hSource, + pNode->dwFileSize ); + + CloseHandle( hSource ); + + ErrorMessage( "\n" ); + + return nClusters; + + } + + +BOOL TrimTheTree( PFILENODE pDir ) { + + PFILENODE pPrev, pNode, pNext; + + pPrev = NULL; + pNode = pDir->pFirstFile; + + while( pNode ) { + + pNext = pNode->pNextFile; + + if (( pNode->dwWin32Attributes & FILE_ATTRIBUTE_DIRECTORY ) && + ( TrimTheTree( pNode ))) { + + MyFree( pNode ); + + if ( pPrev ) + pPrev->pNextFile = pNext; + else + pDir->pFirstFile = pNext; + + } + else { + pPrev = pNode; + } + + pNode = pNext; + + } + + return (( pDir->pFirstFile == NULL ) ? TRUE : FALSE ); + + } + + +void InitializeOverlapped( void ) { + + if ( ! ( olControl.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ))) { + ErrorExit( "\nCreateEvent failed (GLE=%d)\n", GetLastError() ); + } + + dwOverlappedFilePointer = 0; + bOverlappedOutstanding = FALSE; + + pOverlappedBuffer[ 0 ] = VirtualAlloc( NULL, + BUFFER_SIZE, + MEM_COMMIT, + PAGE_READWRITE ); + + pOverlappedBuffer[ 1 ] = VirtualAlloc( NULL, + BUFFER_SIZE, + MEM_COMMIT, + PAGE_READWRITE ); + + if (( pOverlappedBuffer[ 0 ] == NULL ) || + ( pOverlappedBuffer[ 1 ] == NULL )) { + + ErrorExit( "\nOut of memory\n" ); + } + + + iAvailableOverlappedBuffer = 0; + + } + + +void OverlappedVerifyComplete( HANDLE hFile ) { + + DWORD dwActualBytes; + + if ( bOverlappedOutstanding ) { + if ( ! GetOverlappedResult( hFile, &olControl, &dwActualBytes, TRUE )) { + ErrorExit( "\nError overlapped %s %s %s (GLE=%d)\n", + bOverlappedWriting ? "writing" : "reading", + bDestIsDevice ? "device" : "file", + bDestIsDevice ? chDest + 4 : chDest, + GetLastError() ); + } + if ( dwActualBytes != dwOverlappedExpectedBytes ) { + ErrorExit( "\nAssert: overlapped dwActualBytes != dwExpected\n" ); + } + dwOverlappedFilePointer += dwActualBytes; + bOverlappedOutstanding = FALSE; + } + } + + +void OverlappedWriteFile( HANDLE hFile, LPVOID pData, DWORD nBytes ) { + + DWORD dwActualBytes, dwGLE; + + if ( bWriteToTarget ) { + + CopyMemory( pOverlappedBuffer[ iAvailableOverlappedBuffer ], + pData, + nBytes ); + + // + // First, we have to wait for the previous write to complete + // if there's one outstanding. + // + + OverlappedVerifyComplete( hFile ); + + + // + // Now the previous overlapped write is complete so we can + // load up the overlapped structure for our new write + // + + olControl.Offset = dwOverlappedFilePointer; + olControl.OffsetHigh = 0; + + if ( WriteFile( hFile, + pOverlappedBuffer[ iAvailableOverlappedBuffer ], + nBytes, + &dwActualBytes, + &olControl )) { + + // completed synchronously + + if ( dwActualBytes != nBytes ) + ErrorExit( "\nAssert: dwActualBytes != nBytes\n" ); + + dwOverlappedFilePointer += dwActualBytes; + + } + + else if (( dwGLE = GetLastError() ) == ERROR_IO_PENDING ) { + + // overlapped + + dwOverlappedExpectedBytes = nBytes; + bOverlappedOutstanding = TRUE; + bOverlappedWriting = TRUE; + iAvailableOverlappedBuffer = ! iAvailableOverlappedBuffer; // toggle 1-0 + + } + + else { + + // a real error + + ErrorExit( "\nError writing %s %s (GLE=%d)\n", + bDestIsDevice ? "device" : "file", + bDestIsDevice ? chDest + 4 : chDest, + dwGLE ); + } + } + + if ( bWriteToBuffer ) { + CopyMemory( pWholeDiskCurrentLocation, pData, nBytes ); + pWholeDiskCurrentLocation += nBytes; + } + + } + + +void OverlappedReadFile( HANDLE hFile, LPVOID pData, DWORD nBytes, PDWORD pdwActualBytes ) { + + DWORD dwGLE; + + if ((DWORD)pData & 0x1FF ) + ErrorExit( "\nAssert: ReadFile unaligned buffer 0x%08X\n", pData ); + + if ( ROUNDUP2( nBytes, wClusterSize ) != nBytes ) + ErrorExit( "\nAssert: ReadFile 0x%X byte write requested\n", nBytes ); + + // + // First, we have to wait for the previous i/o to complete + // if there's one outstanding. + // + + OverlappedVerifyComplete( hFile ); + + + // + // Now the previous overlapped write is complete so we can + // load up the overlapped structure for our new write + // + + olControl.Offset = dwOverlappedFilePointer; + olControl.OffsetHigh = 0; + + if ( ReadFile( hFile, pData, nBytes, pdwActualBytes, &olControl )) { + + // completed synchronously + + dwOverlappedFilePointer += *pdwActualBytes; + + } + + else if (( dwGLE = GetLastError() ) == ERROR_IO_PENDING ) { + + // overlapped + + dwOverlappedExpectedBytes = nBytes; + bOverlappedOutstanding = TRUE; + bOverlappedWriting = FALSE; + + } + + else { + + // a real error + + ErrorExit( "\nError reading %s %s (GLE=%d)\n", + bDestIsDevice ? "device" : "file", + bDestIsDevice ? chDest + 4 : chDest, + dwGLE ); + } + + } + + +void OverlappedSetFilePointer( HANDLE hFile, DWORD dwAbsoluteOffset ) { + + OverlappedVerifyComplete( hFile ); + dwOverlappedFilePointer = dwAbsoluteOffset; + + } + + +PPARAM LoadParameters( int argc, char *argv[] ) { + + PPARAM pList, pLast, pNew; + char *pArg; + int i; + + pList = MyAllocZ( sizeof( PARAM )); + pLast = pList; + + for ( i = 1; i < argc; i++ ) { + + pArg = argv[ i ]; + + if ( *pArg == '@' ) { + + pLast = ParseResponseFile( pArg + 1, pLast ); + + } + + else { + + pNew = MyAllocZ( sizeof( PARAM )); + + pNew->pszParam = pArg; + pNew->pNext = NULL; + pLast->pNext = pNew; + pLast = pNew; + + } + } + + return pList; + } + + +PPARAM ParseResponseFile( LPSTR pszFileName, PPARAM pLast ) { + + CHAR chBuffer[ MAX_ENV ]; + HANDLE hFile; + PCHAR pFile, pToken, pStartOfThisLine, pStartOfNextLine; + DWORD dwFileSize; + PPARAM pNew; + + hFile = CreateFile( pszFileName, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL ); + + if ( hFile == INVALID_HANDLE_VALUE ) { + ErrorExit( "\nCan't open response file %s (GLE=%d)\n", + pszFileName, + GetLastError() ); + } + + dwFileSize = GetFileSize( hFile, NULL ); + + pFile = MyAllocZ( dwFileSize + 1 ); + + if ( pFile == NULL ) { + ErrorExit( "\nOut of memory\n" ); + } + + if ( ! ReadFile( hFile, pFile, dwFileSize, &dwFileSize, NULL )) { + ErrorExit( "\nCan't read response file %s (GLE=%d)\n", + pszFileName, + GetLastError() ); + } + + CloseHandle( hFile ); + + *( pFile + dwFileSize ) = '\0'; // strtok needs terminator + +#ifdef ANAL + + // + // anal retentive -- if we want to handle embedded NULLs in file + // as simple delimiters as opposed to end-of-file, replace them + // with spaces before processing. + // + + for ( pToken = pFile; pToken < ( pFile + dwFileSize ); pToken++ ) + if ( *pToken == 0 ) + *pToken = ' '; + +#endif + + pStartOfThisLine = pFile; + + while ( pStartOfThisLine ) { + + pStartOfNextLine = strchr( pStartOfThisLine, '\n' ); + if ( pStartOfNextLine ) { + do *pStartOfNextLine++ = '\0'; + while ( *pStartOfNextLine == '\n' ); + } + + pToken = strtok( pStartOfThisLine, " \t\r\f\v" ); + + while ( pToken ) { + + if (( *pToken == '#' ) || + ( *pToken == ';' ) || + ( *(UNALIGNED WORD *) pToken == 0x2F2F )) { + + // + // '#' ';' "//" (0x2F2F) all indicate comment to end of line + // + + break; + } + + ReplaceEnvironmentStrings( pToken, chBuffer ); + pNew = MyAllocZ( sizeof( PARAM )); + pNew->pszParam = DuplicateString( chBuffer ); + pNew->pNext = NULL; + pLast->pNext = pNew; + pLast = pNew; + pToken = strtok( NULL, " \t\r\f\v" ); + } + + pStartOfThisLine = pStartOfNextLine; + } + + MyFree( pFile ); + return pLast; + } + + +PPARAM RemoveParam( PPARAM pThis, PPARAM pPrev ) { + PPARAM pReturnNext = pPrev->pNext = pThis->pNext; + MyFree( pThis ); + return pReturnNext; + } + + +LPSTR ReplaceEnvironmentStrings( LPCSTR pszInputString, LPSTR pTargetBuffer ) { + + CHAR szVariableName[ MAX_ENV ]; + LPCSTR pSource = pszInputString; + LPSTR pDestin = pTargetBuffer; + LPCSTR pName; + DWORD dwLen; + + while (( pName = strchr( pSource, '%' )) != NULL ) { + + memcpy( pDestin, pSource, ( pName - pSource )); + pDestin += ( pName - pSource ); + pSource = strchr( pName + 1, '%' ); + + if ( pSource == NULL ) { + pSource = pName; + break; + } + else if ( pSource == ( pName + 1 )) { + *pDestin++ = '%'; + } + else { + memcpy( szVariableName, pName + 1, ( pSource - pName - 1 )); + *( szVariableName + ( pSource - pName - 1 )) = '\0'; + dwLen = GetEnvironmentVariable( szVariableName, pDestin, MAX_ENV ); + if ( dwLen > MAX_ENV ) + ErrorExit( "\n%s: environment variable value too long\n", + szVariableName ); + pDestin += dwLen; + } + + ++pSource; + } + + strcpy( pDestin, pSource ); + return pTargetBuffer; + + } + + +BOOL AddDirectoryToList( LPSTR pszPathName, + PFILENODE pParentDir, + UINT nSourceIndex, + PFILENODE *pThisNode ) { + + PFILENODE pPrev, pNext, pNew; + CHAR chFileName[ MAX_PATH ]; + LPSTR pRestOfPath; + LPSTR pExtension; + INT iCompare; + + while ( *pszPathName == '\\' ) + ++pszPathName; + + strcpy( chFileName, pszPathName ); + pRestOfPath = strchr( chFileName, '\\' ); + + if ( pRestOfPath ) + *pRestOfPath++ = '\0'; + + _strupr( chFileName ); + pExtension = strchr( chFileName, '.' ); + + if ((( pExtension == NULL ) && ( strlen( chFileName ) > 8 )) || + (( pExtension ) && + (( pExtension - chFileName > 8 ) || + ( strlen( pExtension + 1 ) > 3 )))) { + + ErrorExit( "\n%s: invalid pathname\n", chFileName ); + } + + for ( pPrev = NULL, pNext = pParentDir->pFirstFile; + pNext != NULL; + pPrev = pNext, pNext = pNext->pNextFile ) { + + iCompare = strcmp( pNext->pszTargetFileName, chFileName ); + + if ( ! iCompare ) { + + // + // This file or pathname already exists. Verify + // that it is a directory versus a file, then + // process remainder of path if any, else mark + // this node and return FALSE indicating already + // existed. + // + + if ( pNext->dwWin32Attributes != FILE_ATTRIBUTE_DIRECTORY ) { + ErrorExit( "\n%s: directory and file have same name\n", + chFileName ); + } + + if ( pRestOfPath ) { + return AddDirectoryToList( pRestOfPath, + pNext, // parent + nSourceIndex, + pThisNode ); + } + else { + if ( pThisNode ) + *pThisNode = pNext; + + return FALSE; + } + } + + else if (( pNext->nSourceIndex >= nSourceIndex ) && + ( iCompare > 0 )) { + break; + } + + } + + pNew = NewFileNode(); + pNew->pszTargetFileName = DuplicateString( chFileName ); + pNew->dwFileSize = 0; + pNew->dwWin32Attributes = FILE_ATTRIBUTE_DIRECTORY; + pNew->ftWin32TimeStamp = ftGlobalFileTime; + pNew->nSourceIndex = nSourceIndex; + pNew->pParentDir = pParentDir; + pNew->pNextFile = pNext; + + if ( pPrev ) + pPrev->pNextFile = pNew; + else + pParentDir->pFirstFile = pNew; + + if ( pRestOfPath ) { + return AddDirectoryToList( pRestOfPath, + pNew, // parent + nSourceIndex, + pThisNode ); + } + else { + if ( pThisNode ) + *pThisNode = pNew; + + return TRUE; + } + + } + +void Copyright( void ) { + printf( "\n" + "FCOPY Diskette Mastering Utility Version 1.54" +#ifdef DMFSIGNATURE + "s" +#endif + "\n" + "Copyright (C) Microsoft, 1993-1996. All rights reserved.\n" + "For Microsoft internal use only.\n" + "\n" ); + } + + + diff --git a/private/windows/diamond/dmftools/fcopy/makefile b/private/windows/diamond/dmftools/fcopy/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/windows/diamond/dmftools/fcopy/precomp.h b/private/windows/diamond/dmftools/fcopy/precomp.h new file mode 100644 index 000000000..e34941dbb --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/precomp.h @@ -0,0 +1,6 @@ +#include <windows.h> +#include <winioctl.h> +#include <stdlib.h> +#include <stdio.h> +#include "dmffmt.h" + diff --git a/private/windows/diamond/dmftools/fcopy/sources b/private/windows/diamond/dmftools/fcopy/sources new file mode 100644 index 000000000..cd2f7f9ed --- /dev/null +++ b/private/windows/diamond/dmftools/fcopy/sources @@ -0,0 +1,20 @@ +MAJORCOMP=setup +MINORCOMP=fcopy + +TARGETNAME=fcopy +TARGETPATH=obj +TARGETTYPE=PROGRAM + +SOURCES=fcopy.c \ + dmffmt.c \ + dmfsign.c + +C_DEFINES=$(C_DEFINES) -DDMFSIGNATURE=1 + +USE_CRTDLL=1 + +UMTYPE=console + +PRECOMPILED_INCLUDE=precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/windows/diamond/error.c b/private/windows/diamond/error.c new file mode 100644 index 000000000..0cab6e7ab --- /dev/null +++ b/private/windows/diamond/error.c @@ -0,0 +1,64 @@ +/*** error.c - Error Reporting + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 10-Aug-1993 bens Initial version + * 03-May-1994 bens Add err.code and err.pv fields + */ + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "message.h" + + +/*** ErrSet - Set error message + * + * NOTE: See error.h for entry/exit conditions. + */ +void __cdecl ErrSet(PERROR perr, char *pszMsg, ...) +{ + va_list marker; + char *pszFmtList; + + Assert(perr!=NULL); + Assert(pszMsg!=NULL); + + va_start(marker,pszMsg); // Initialize variable arguments + pszFmtList = (char *)va_arg(marker,char *); // Assume format string + + //** Format the message + MsgSetWorker(perr->ach,pszMsg,pszFmtList,marker); + va_end(marker); // Done with variable arguments + perr->fError = TRUE; +} + + +/*** ErrClear - Clear ERROR + * + * NOTE: See error.h for entry/exit conditions. + */ +void ErrClear(PERROR perr) +{ + Assert(perr != NULL); + perr->fError = FALSE; // No error + perr->ach[0] = '\0'; // No message + perr->code = 0; + perr->pv = NULL; +} + + +#ifdef ASSERT +/*** ErrIsError - Check if error condition is set + * + * NOTE: See error.h for entry/exit conditions. + */ +BOOL ErrIsError(PERROR perr) +{ + Assert(perr != NULL); + return perr->fError; +} +#endif diff --git a/private/windows/diamond/error.h b/private/windows/diamond/error.h new file mode 100644 index 000000000..6589b9f0d --- /dev/null +++ b/private/windows/diamond/error.h @@ -0,0 +1,85 @@ +/*** error.h - Definitions for Error Reporting + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 10-Aug-1993 bens Initial version + * 09-Feb-1994 bens Add pszLine to ERROR structure + * 03-May-1994 bens Add err.code and err.pv fields + */ + +#ifndef INCLUDED_ERROR +#define INCLUDED_ERROR 1 + +#include "message.h" + +typedef struct { + char ach[cbMSG_MAX]; // Error message + BOOL fError; // TRUE => error present + char *pszFile; // Name of directives file being processed + int iLine; // Line number in directives file, if >0 + char *pszLine; // Text of current line being processed + int code; // Detailed error code + void *pv; // Additional error information +} ERROR; /* err */ +typedef ERROR *PERROR; /* perr */ + + +/*** ErrSet - Set error message + * + * Entry + * perr - ERROR structure to receive formatted message + * pszMsg - Message string, possibly including %1, %2, ... replaceable + * parameters. + * Remaining arguments are optional, and depend upon presence of %N + * replaceable parameters in pszMsg: + * pszFmt - If at least one %N string in pszMsg, then this contains + * sprintf() formatting strings. + * Arg1 - Present only if %1 is present. + * Arg2 - Present only if %2 is present. + * ... + * + * Exit-Success + * perr filled in with formatted message. + * Arg1 is formatted according to the first sprintf format in + * pszFmt, and replaces the %1 in pszMsg. Similar treatment for + * any other arguments. + * + * Exit-Failure + * perr filled in with message describing bad arguments. + */ +void __cdecl ErrSet(PERROR perr, char *pszMsg, ...); + + +/*** ErrClear - Clear ERROR + * + * Entry + * perr - ERROR structure to clear + * + * Exit-Success + * perr is cleared + */ +void ErrClear(PERROR perr); + + +/*** ErrIsError - Check if error condition is set + * + * Entry + * perr - ERROR structure to check + * + * Exit-Success + * Returns TRUE if an error message is set. + * + * Exit-Failure + * Returns FALSE if no error message set. + */ +#ifdef ASSERT +BOOL ErrIsError(PERROR perr); +#else // !ASSERT +#define ErrIsError(perr) (perr->fError) // Fast dereference +#endif // !ASSERT + + +#endif // !INCLUDED_ERROR diff --git a/private/windows/diamond/extract.c b/private/windows/diamond/extract.c new file mode 100644 index 000000000..bbeab8b49 --- /dev/null +++ b/private/windows/diamond/extract.c @@ -0,0 +1,2520 @@ +/*** extract.c - Main program for EXTRACT.EXE + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 19-Feb-1994 bens Initial version (started with diamond.c) + * 22-Feb-1994 bens Implement file extract + * 03-Mar-1994 bens Split cabinet paths from cabinet file names + * 08-Mar-1994 bens Add date/time/attribute display + * 09-Mar-1994 bens Improve response to FDI errors + * 16-Mar-1994 bens More FDI error codes + * 21-Mar-1994 bens Log all open/close calls to see if we are + * losing file handles in FDI.LIB. + * 28-Mar-1994 bens Handle fdintCABINET_INFO, support /A switch + * 30-Mar-1994 bens Move MS-DOS/Win32 knowledge to fileutil.* + * 31-Mar-1994 bens Add SMALL_DOS to test small model FDI client + * 01-Apr-1994 bens Add /D and /E switches, support full command + * line behavior. + * 07-Apr-1994 bens Add crypto support (at least for debugging) + * 06-May-1994 bens Improve /D display for long filenames + * 13-May-1994 bens Add prompting for next cabinet, DMF support + * 27-May-1994 bens Include correct strings for localization + * 03-Jun-1994 bens Report error on correct cabinet + * 07-Jun-1994 bens Localization enabled + * 21-Jun-1994 bens Localization enabled + * 08-Jul-1994 bens Quantum Spill File, self-extracting cabinets! + * 11-Jul-1994 bens Use 24-hour time format if am/pm strings are empty + * 26-Jul-1994 bens Add /C switch; no switches give /? help + * 05-Aug-1994 bens Chicago bug 13214 (don't show partial file info + * unless name matches request). Chicago bug 13221 + * (truncate extracted file to specified size, in + * case file already existed and was larger!). + * Chicago bug 9646 (give details of Quantum + * decompress failure -- out of RAM, spill file). + * Implement overwrite prompt and /Y switch. + * 14-Dec-1994 bens Include Floppy changeline fix from + * ..\dmf\dmftsr\fixchg.c + * 12-Mar-1995 bens Define NOT_US_PC flag to disable use of DMF hook + * and FixChangeline code. Also, check COMSPEC to + * detect boot drive, instead of hard-coding C:, so + * that the Quantum spill file can default to the + * boot drive if no TEMP path is found. In the far + * east, the hard disk boot drive is A:, so that's + * why we have to check! + * 31-Mar-1995 jeffwe Fix Command line ambiguity when no /D or /E + * option is specified + * 2-Apr-1995 jeffwe Fix file time/date set to change the correct + * file when rename option being used + * + * + * Notes: + * A self-extracting cabinet file can be created using DIAMOND.EXE and + * EXTRACT.EXE very simply: + * 1) Create a cabinet file using DIAMOND.EXE + * 2) COPY /B EXTRACT.EXE foo.cab foo.exe + * When EXTRACT starts executing, it compares the file size indicated + * in the EXE headers (MZ or PE, as appropriate) with the size of the + * file indicated in argv[0]. If the argv[0] size is greater, and a + * cabinet file appears there, then EXTRACT goes into self-extracting + * mode! + */ + +//** Definitions for the banner -- see extract.msg:pszBANNER + +#define szBANNER_VERSION "1.00.0540" +#define szBANNER_MONTH "02" +#define szBANNER_DAY "01" +#define szBANNER_YEAR "96" +#define szBANNER_COPYRIGHT_YEARS "1994-1996" + +#ifdef BIT16 +#define szBANNER_BITNESS "16" // 16-bit version +#else +#define szBANNER_BITNESS "32" // 32-bit version +#endif + +#ifdef NOT_US_PC +#define szBANNER_DMF_INDICATOR "n" // Not IBM BIOS version (i.e., NEC) +#else +#define szBANNER_DMF_INDICATOR "" +#endif + + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <malloc.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <errno.h> +#include <direct.h> +#include <conio.h> + +#ifdef BIT16 +#include <dos.h> +#include "fixchg.h" +#else // !BIT16 +//** Get minimal Win32 definitions +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ERROR // Override stupid "#define ERROR 0" in wingdi.h +#endif // !BIT16 + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "filelist.h" +#include "fileutil.h" + +#include "dmfon.h" // DMF support + +#include <extract.msg> // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath" + +#ifdef BIT16 +#include "chuck\fdi.h" +#else // !BIT16 +#include "chuck\nt\fdi.h" +#endif // !BIT16 + + +//** Constants + +#define cbMAX_LINE 256 // Maximum output line length + + +#define cMAX_CAB_FILE_OPEN 2 // Maximum simultaneous opens on a single + // cabinet file + +//** Error causes for failure of spill file +typedef enum { + seNONE, // No error + seNOT_ENOUGH_MEMORY, // Not enough RAM + seCANNOT_CREATE, // Cannot create spill file + seNOT_ENOUGH_SPACE, // Not enough space for spill file +} SPILLERR; /* se */ + + +//** Types + +typedef enum { + actBAD, // Invalid action + actHELP, // Show help + actDEFAULT, // Perform default action based on command line arguments + actDIRECTORY, // Force display of cabinet directory + actEXTRACT, // Force file extraction + actCOPY, // Do single file-to-file copy +} ACTION; /* act */ + + +typedef struct { + char achCabPath[cbFILE_NAME_MAX]; // Cabinet file path + char achCabFilename[cbFILE_NAME_MAX]; // Cabinet file name.ext + char achDiskName[cbFILE_NAME_MAX]; // User readable disk label + USHORT setID; + USHORT iCabinet; +} CABINET; /* cab */ +typedef CABINET *PCABINET; /* pcab */ + + +#ifdef ASSERT +#define sigSESSION MAKESIG('S','E','S','S') // SESSION signature +#define AssertSess(psess) AssertStructure(psess,sigSESSION); +#else // !ASSERT +#define AssertSess(psess) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigSESSION +#endif + ACTION act; // Action to perform + HFILELIST hflist; // List of files specified on cmd line + BOOL fAllCabinets; // TRUE => Process continutation cabs + BOOL fOverwrite; // TRUE => Overwrite existing files + BOOL fNoLineFeed; // TRUE if last printf did not have \n + BOOL fSelfExtract; // TRUE if self-extracting + long cbSelfExtract; // Size of EXE portion of self-ex cabinet + int ahfSelf[cMAX_CAB_FILE_OPEN]; // Cabinet file handles + int cErrors; // Count of errors encountered + HFDI hfdi; // FDI context + ERF erf; // FDI error structure + long cFiles; // Total files processed + long cbTotalBytes; // Total bytes extracted + PERROR perr; // Pass through FDI + SPILLERR se; // Spill file error + long cbSpill; // Size of spill file requested + char achSelf[cbFILE_NAME_MAX]; // Name of our EXE file + char achMsg[cbMAX_LINE*2]; // Message formatting buffer + char achLine[cbMAX_LINE]; // Line formatting buffer + char achLocation[cbFILE_NAME_MAX]; // Output directory + char achFile[cbFILE_NAME_MAX]; // Current filename being extracted + char achDest[cbFILE_NAME_MAX]; // Forced destination file name + char achCabPath[cbFILE_NAME_MAX]; // Path to look for cabinet file + + BOOL fContinuationCabinet; // TRUE => not 1st cabinet processed + BOOL fShowReserveInfo; // TRUE => show RESERVEd cabinet info + + //** fNextCabCalled allows us to figure out which of the acab[] entries + // to use if we are processing all file in a cabinet set (i.e., if + // fAllCabinet is TRUE). If fdintNEXT_CABINET has never been called, + // then acab[1] has the information for the next cabinet. But if + // it has been called, then fdintCABINET_INFO will have been called + // at least twice (once for the first cabinet, and once at least for + // a continuation cabinet), and so acab[0] is the cabinet we need to + // pass to a subsequent FDICopy() call. + BOOL fNextCabCalled; // TRUE => GetNextCabinet called + CABINET acab[2]; // Last two fdintCABINET_INFO data sets + +} SESSION; /* sess */ +typedef SESSION *PSESSION; /* psess */ + + +/* + ** Spill file statics for Quantum + */ +int hfSpillFile; // File handle +char achSpillFile[cbFILE_NAME_MAX]; // File path + +/* + ** Global state for self-extract + */ +PSESSION psessG; + + +//** Function Prototypes + +FNASSERTFAILURE(fnafReport); + +HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr); +BOOL checkWildMatches(PSESSION psess, char *pszFile, PERROR perr); +BOOL doCabinet(PSESSION psess, PERROR perr); +BOOL doCopy(PSESSION psess, PERROR perr); +BOOL ensureCabinet(PSESSION psess, + char *pszPath, + int cbPath, + char *pszFile, + char *pszLabel, + USHORT setID, + USHORT iCabinet, + BOOL fLoop, + BOOL fPromptOnly, + PERROR perr); +BOOL checkOverwrite(PSESSION psess, + char *pszFile, + PERROR perr, + int *prc); +BOOL checkSelfExtractingCab(PSESSION psess, + int cArg, + char *apszArg[], + PERROR perr); +char *getBootDrive(void); +BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr); +void printError(PSESSION psess, PERROR perr); +void pszFromAttrFAT(char *psz, int cb, WORD attrFAT); +void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time); +int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin); + + +//** FDI callbacks and related functions +FNALLOC(fdiAlloc); +FNFREE(fdiFree); +FNFDINOTIFY(fdiNotifyDir); +FNFDINOTIFY(fdiNotifyExt); +FNFDINOTIFY(doGetNextCab); + +FNFDIDECRYPT(fdiDecryptDir); +FNFDIDECRYPT(fdiDecryptExt); + +void mapFDIError(PERROR perr,PSESSION psess, char *pszCabinet, PERF perf); + + +//** File I/O wrapper functions +int FAR DIAMONDAPI wrap_open(const char FAR *, int, int); +UINT FAR DIAMONDAPI wrap_read(int, void FAR *, unsigned int); +UINT FAR DIAMONDAPI wrap_write(int, const void FAR *, unsigned int); +int FAR DIAMONDAPI wrap_close(int); +long FAR DIAMONDAPI wrap_lseek(int, long, int); + +#ifdef SMALL_DOS +#define STRCPY(dst,src) _fstrcpy((char far *)dst,(char far *)src) +#else +#define STRCPY(dst,src) strcpy(dst,src) +#endif + +//BUGBUG 08-Jul-1994 bens Generate debug output +//#define DEBUG_FDI 1 + +#ifdef DEBUG_FDI +#define dbg(a) a +#else +#define dbg(a) +#endif + +//** Functions + +/*** main - Extract main program + * + * See DIAMOND.DOC for spec and operation. + * + * NOTE: We're sloppy, and don't free resources allocated by + * functions we call, on the assumption that program exit + * will clean up memory and file handles for us. + */ +int __cdecl main(int cArg, char *apszArg[]) +{ + char ach[cbMSG_MAX]; + ERROR err; + PSESSION psess; + +// #define NTVCPP_DEBUG_HACK +#ifdef NTVCPP_DEBUG_HACK + _chdir("\\elroy\\diamond\\layout\\testnew"); +#endif + + AssertRegisterFunc(fnafReport); // Register assertion reporter + ErrClear(&err); // No error + err.pszFile = NULL; // No file being processed, yet + achSpillFile[0] = '\0'; // No name constructed, yet + +#ifdef BIT16 +#ifndef NOT_US_PC + //** Make sure we can read DMF disks -- only for pre-Chicago systems + EnableDMFSupport(); + + //** Turn off floppy disk changeline support to make sure systems + // with faulty change lines don't choke on disk 2 in the case + // where disk 1 is non-DMF and disk 2 is DMF. + FixChangelines(); +#endif +#endif + + //** Initialize session + psess = MemAlloc(sizeof(SESSION)); + if (!psess) { + ErrSet(&err,pszEXTERR_NO_SESSION); + printError(psess,&err); + exit(1); + } + SetAssertSignature((psess),sigSESSION); + psessG = psess; // Save for wrap_open/wrap_close + psess->fOverwrite = FALSE; // Default to being save + psess->fAllCabinets = FALSE; // Don't do continuation cabinets + psess->fNextCabCalled = FALSE; + psess->fContinuationCabinet = FALSE; + psess->fShowReserveInfo = FALSE; + psess->fSelfExtract = FALSE; + psess->hflist = NULL; + psess->hfdi = NULL; + psess->fNoLineFeed = 0; // TRUE if last printf did not have \n + psess->cFiles = 0; // No files, yet + psess->cbTotalBytes = 0; // No bytes, yet + psess->se = seNONE; // No spill file error + + //** Print Extract banner + MsgSet(ach,pszBANNER,"%s%s%s%s%s%s%s", + szBANNER_BITNESS, + szBANNER_VERSION, + szBANNER_DMF_INDICATOR, + szBANNER_MONTH, + szBANNER_DAY, + szBANNER_YEAR, + szBANNER_COPYRIGHT_YEARS); + printf(ach); + + //** Parse command line + if (!parseCommandLine(psess,cArg,apszArg,&err)) { + printError(psess,&err); + return 1; + } + + //** Quick out if command line help is requested + if (psess->act == actHELP) { // Do help if any args, for now + printf("\n"); // Separate banner from help + printf(pszCMD_LINE_HELP); + return 0; + } + + //** Quick out for COPY command + if (psess->act == actCOPY) { + if (!doCopy(psess,&err)) { + printError(psess,&err); + return 1; + } + //** Success + return 0; + } + + //** Have some work to do -- go do it + if (!doCabinet(psess,&err)) { + printError(psess,&err); + //** Make sure we delete spill file + if (hfSpillFile != -1) { + wrap_close(hfSpillFile); // Close and delete it + } + return 1; + } + + //** See if we actually got any files + if (psess->cFiles == 0) { + MsgSet(psess->achMsg,pszEXT_NO_MATCHING_FILES); + printf("%s\n",psess->achMsg); + } + else if (psess->act == actDIRECTORY) { + //** Print out file and byte count + MsgSet(psess->achMsg, + psess->cFiles == 1 ? pszEXT_SUMMARY1 : pszEXT_SUMMARY2, + "%,13ld%,13ld", + psess->cFiles, psess->cbTotalBytes); + printf("%s\n",psess->achMsg); + } + + //** Free resources + AssertSess(psess); + ClearAssertSignature((psess)); + MemFree(psess); + + //** Success + return 0; +} /* main */ + + +/*** doCopy - Copy one file + * + * Entry: + * psess - Description of operation to perform + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; file copied + * + * Exit-Failure: + * Returns FALSE; error + * + * NOTE: + * Supported SRC/DST syntax: + * Src Dst Example + * ---- ---- -------------------------- + * file dir "foo.exe ."; "foo.exe c:\dir" + * file file "foo.exe c:bar.exe" + */ +BOOL doCopy(PSESSION psess, PERROR perr) +{ + char achDst[cbFILE_NAME_MAX]; // Buffer for src file name + HFILESPEC hfspec; + char *pszSrc; + char *pszSrcJustFile; + char *pszDst; + int rc; + struct _stat stat; + + //** Get the source file + hfspec = FLFirstFile(psess->hflist); + Assert(hfspec != NULL); + pszSrc = FLGetSource(hfspec); + Assert(pszSrc!=NULL); + Assert(*pszSrc); + + //** Get the destination file + hfspec = FLNextFile(hfspec); // We have something + Assert(hfspec != NULL); + pszDst = FLGetSource(hfspec); + Assert(pszDst!=NULL); + Assert(*pszDst); + + //** Determine if destination is a directory + if (-1 != _stat(pszDst,&stat)) { // File/Dir exists + //** Destination exists + if (stat.st_mode & _S_IFDIR) { // It is a directory + //** It is a directory; get just file name and extension of source + if (!(pszSrcJustFile = getJustFileNameAndExt(pszSrc,perr))) { + return FALSE; + } + //** Construct destination name + if (!catDirAndFile( + achDst, // Buffer for full path + sizeof(achDst), // Size of buffer + pszDst, // Destination directory + pszSrcJustFile, // File name + NULL, // Don't have alternate name + perr)) { + return FALSE; // Failure + } + //** Use constructed name + pszDst = achDst; + } + } + + //** Make sure it's OK to overwrite destination file + if (!checkOverwrite(psess,pszDst,perr,&rc)) { + //** Ignore skip/abort return code in rc + return TRUE; // Skip file copy, everything is OK + } + + //** Tell user we're copying the file + MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE2,"%s%s",pszSrc,pszDst); + printf("%s\n",psess->achMsg); + + //** Do the file copy + return CopyOneFile(pszDst, // destination + pszSrc, // source + TRUE, // do the copy + 32768U, // copy buffer size + NULL, // don't override date/time/attr + NULL, // no callback context + perr); +} /* doCopy() */ + + +/*** doCabinet - Show contents of one or more cabinet files + * + * Entry: + * psess - Description of operation to perform + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; directory displayed + * + * Exit-Failure: + * Returns FALSE; perr filled in with details. + */ +BOOL doCabinet(PSESSION psess, PERROR perr) +{ + char achFile[cbFILE_NAME_MAX]; // Buffer for cabinet file + FDICABINETINFO fdici; + BOOL fCompatibilityCabinet; // TRUE => Exactly 1 file in 1 cabinet + int hfCab = -1; // File handle for peeking at cabinet + HFILESPEC hfspec; + int iCab; + PFNFDINOTIFY pfnfdin; + PFNFDIDECRYPT pfnfdid; + char FAR *pbMemReserve; // Make sure we have some working mem + char *pszCabinet; // Cabinet filespec + char *pszCabFile; // Cabinet filename.ext + char *pszDestOrPattern; // Destination or first pattern + + //** Get the cabinet name + hfspec = FLFirstFile(psess->hflist); + Assert(hfspec != NULL); // Must have at least one file + pszCabinet = FLGetSource(hfspec); + Assert(pszCabinet!=NULL); + Assert(*pszCabinet); + + //** Get the destination file name or first pattern, if present + if (NULL != (hfspec = FLNextFile(hfspec))) { // We have something + pszDestOrPattern = FLGetSource(hfspec); + Assert(pszDestOrPattern!=NULL); + //** NOTE: hfspec must remain pointing to this 2nd file all the + // way down below where we may need to change its value! + } + else { + pszDestOrPattern = NULL; // No second argument on command line + } + + //** Remember that we have not yet created a spill file + hfSpillFile = -1; // No spill file, yet + + //** Prevent FDI from sucking up all available memory + // Why 2048? That's enough for 4 paths, which is more than we + // will ever need. + pbMemReserve = fdiAlloc(2048); + if (!pbMemReserve) { + ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet); + return FALSE; + } + + //** Create FDI context so we can get info from the cabinet file + if (!(psess->hfdi = FDICreate(fdiAlloc, + fdiFree, + wrap_open, + wrap_read, + wrap_write, + wrap_close, + wrap_lseek, + cpuUNKNOWN, // Let FDI do the CPU detection + &(psess->erf) + ))) { + //** FDICreate failed, generate error message + mapFDIError(perr,psess,pszCabinet,&(psess->erf)); + fdiFree(pbMemReserve); // Free reserved memory + return FALSE; + } + fdiFree(pbMemReserve); // Free it so we can use it + + //** Make sure file is a cabinet, and get cabinet info + if (-1 == (hfCab = wrap_open(pszCabinet,_O_BINARY | _O_RDONLY,0))) { + ErrSet(perr,pszEXTERR_CANNOT_OPEN_FILE,"%s",pszCabinet); + goto cleanup; + } + if (!FDIIsCabinet(psess->hfdi,hfCab,&fdici)) { + if (!ErrIsError(perr)) { // Have to set error message + ErrSet(perr,pszEXTERR_NOT_A_CABINET,"%s",pszCabinet); + } + goto cleanup; + } + wrap_close(hfCab); + hfCab = -1; + + //** No Default Destination + psess->achDest[0] = '\0'; + + //** If no mode specified, figure out what mode we should be in: + // + // The extract command has ambiguous syntax so we apply the following + // rules to resolve the ambiguity. + // + // Most cabinet file authors use DIAMOND.EXE to create a set of + // cabinet files with many files in it. A typical cabinet set would + // look like: + // (1) Cab#1 + // foo.1 + // foo.2 + // foo.3 - partial + // Cab#2 + // foo.3 - continued + // ... + // + // However, there are some "old-style" customers of DIAMOND.EXE that + // like to compress each file independently, producing a "set" of + // cabinet files that each contain exactly one file, i.e.: + // (2) excel.ex_ + // excel.in_ + // setup.in_ + // + // The "_" character in the extension is a hint that the file is + // compressed. However, this isn't useful to this program + // + // Now, the question is, what does the customer want to have happen + // when she types "EXTRACT foo.cab bar"? For the multi-file cabinet + // case (1) above, this means "seach foo.cab and extract all files + // that are named bar". BUT, for the case (2), we have a compatibility + // constraint -- she thinks this means "extract the compressed + // file foo.cab and call the resulting uncompressed file bar". + // + // Another question is what does the customer want to have happen + // when she types "EXTRACT foo.cab"? For the multi-file cabinet + // case (1) above this means list the contents of the cabinet. + // But for case (2), we have a compatibility constraint -- customers + // think this means "extract the compressed file foo.cab". + // + // + // A cabinet is of type (1) if it contains more than one file, + // or has either a previous or next cabinet. Otherwise, it is of + // type (2), i.e., the cabinet has exactly one file, and has no + // previous or next cabinet. + + if (psess->act == actDEFAULT) { // No action specified on command line + //** Determine if cabinet is of type (2) described above. + fCompatibilityCabinet = (fdici.cFiles == 1) && + (! (fdici.hasprev || fdici.hasnext)); + + //** Now figure out what customer really wants + if (pszDestOrPattern) { // extract foo.cab bar + psess->act = actEXTRACT; + if (fCompatibilityCabinet) { + // Special Case Rename (see above (2)) + strcpy(psess->achDest, pszDestOrPattern); + if (!FLSetSource(hfspec,pszALL_FILES,perr)) { + goto cleanup; + } + } + } else { // extract foo.cab + if (fCompatibilityCabinet) { + // Special Case Extract (see above (2)) + psess->act = actEXTRACT; + } else { + psess->act = actDIRECTORY; + } + } + } + + //** Supply a default pattern if no pattern is present + if (!pszDestOrPattern) { + if (addFileSpec(psess,pszALL_FILES,perr) == NULL) { + ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",pszALL_FILES); + goto cleanup; + } + } + + //** Now, select the appropriate FDI notification function + Assert((psess->act == actEXTRACT) || (psess->act == actDIRECTORY)); + pfnfdin = (psess->act == actEXTRACT) ? fdiNotifyExt : fdiNotifyDir; + if (fdici.fReserve) { // Reserved area(s) present + pfnfdid = (psess->act == actEXTRACT) ? fdiDecryptExt : fdiDecryptDir; + } + else { + pfnfdid = NULL; // No reserved areas + } + + //** Split cabinet spec into path and filename.ext + pszCabFile = getJustFileNameAndExt(pszCabinet,perr); + if (pszCabFile == NULL) { + goto cleanup; // perr is already filled in + } + strcpy(achFile,pszCabFile); + + //** Need to trim off file name and keep just cabinet path + strcpy(psess->achCabPath,pszCabinet); // Store in our buffer + psess->achCabPath[pszCabFile - pszCabinet] = '\0'; // Trim off file name + + psess->perr = perr; // Pass perr through FDI + //** Do cabinets until there are no more, or an error occurs + while ( (strlen(achFile) > 0) && !ErrIsError(perr) ) { + //** Show which cabinet we are processing + MsgSet(psess->achMsg,pszEXT_CABINET_HEADER,"%s",achFile); + printf("\n%s\n\n",psess->achMsg); + + //** Do the cabinet + if (!FDICopy(psess->hfdi, // FDI context + achFile, // Cabinet file name.ext + psess->achCabPath, // Path to cabinet + 0, // Flags (???) + pfnfdin, // Notifcation callback + pfnfdid, // Decrypt callback + psess // Our context + )) { + //** NOTE: psess->achCabPath *may* get changed during an + // fdintNEXT_CABINET callback if we had to prompt the + // use for a different path! + + //** FDICopy failed, construct error message + if (!ErrIsError(perr)) { // Need to set error message + if (psess->fNextCabCalled) { + //** A continuation cabinet had the problem + catDirAndFile( + achFile, // Buffer for full path + sizeof(achFile), // Size of buffer + psess->acab[1].achCabPath, // Cabinet path + psess->acab[1].achCabFilename, // Cabinet file name + NULL, // Don't have alternate name + perr); + //** Ignore any errrors + } + //** Construct error message + mapFDIError(perr,psess,achFile,&(psess->erf)); + + //** Delete file if created, with the assumption that + // we were not able to completely write the file + // (for example, if the destination disk ran out of space!) + if (psess->erf.erfOper == FDIERROR_TARGET_FILE) { + //** Ignore errors, if any + _unlink(psess->achFile); + } + } + } + else { + //** OK so far, see if any more cabinets to process + if (psess->fAllCabinets) { + //** Skip "starts in ..." messages for subsequent cabinets + psess->fContinuationCabinet = TRUE; + + //** Copy next cabinet file (ach[] is empty if no more!) + iCab = psess->fNextCabCalled ? 0 : 1; // Select correct cabinet + strcpy(achFile,psess->acab[iCab].achCabFilename); + psess->fNextCabCalled = FALSE; // Reset flag + + //** If there is another cabinet to process, make sure it + // is available; psess->achCabPath may be edited if we + // can't find the cabinet until the user supplies another + // path; perr will be set if an error occurs. + if (achFile[0] != '\0') { // Another cabinet + ensureCabinet(psess, + psess->achCabPath, + sizeof(psess->achCabPath), + achFile, + psess->acab[iCab].achDiskName, + psess->acab[iCab].setID, + (USHORT)(psess->acab[iCab].iCabinet+1), + TRUE, // Loop until right cab or abort + FALSE, // Check cabinet + perr); + } + } + else { + achFile[0] = '\0'; // Done + } + } + } + +cleanup: + if (hfCab != -1) { + wrap_close(hfCab); + } + + if (!FDIDestroy(psess->hfdi)) { + //** Only set error if we don't already have one + if (!ErrIsError(perr)) { + ErrSet(perr,pszEXTERR_FDIDESTROY_FAILED); + } + } + psess->perr = NULL; + + //** Return success/failure indication + return !ErrIsError(perr); +} /* doCabinet() */ + + +/*** fdiNotifyDir - Callback from FDICopy for Directory display + * + * Entry: + * fdint - type of notification + * pfdin - data for notification + * + * Exit-Success: + * Return value varies (see FDI.H:PFNFDINOTIFY type) + * + * Exit-Failure: + * Return value varies (see FDI.H:PFNFDINOTIFY type) + */ +FNFDINOTIFY(fdiNotifyDir) +{ + char achAttr[10]; + PERROR perr; +#ifdef SMALL_DOS + PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv; + char szLocal[cbFILE_NAME_MAX]; +#else + PSESSION psess=(PSESSION)pfdin->pv; +#endif + + AssertSess(psess); + perr = psess->perr; + + switch (fdint) { + case fdintCABINET_INFO: + return updateCabinetInfo(psess,pfdin); + + case fdintCOPY_FILE: + //** See if filspec matches specified patterns +#ifdef SMALL_DOS + _fstrcpy(szLocal,pfdin->psz1); +#else +#define szLocal pfdin->psz1 +#endif + if (!checkWildMatches(psess,szLocal,perr)) { + //** Either no match, or failure -- figure out which + if (ErrIsError(perr)) { + return -1; // Error, abort + } + else { + return 0; // No error, skip this file + } + } + + //** Show directory + pszFromMSDOSTime(psess->achMsg, + sizeof(psess->achMsg), + pfdin->date, + pfdin->time); + pszFromAttrFAT(achAttr, sizeof(achAttr), pfdin->attribs); + MsgSet(psess->achLine, + pszEXT_FILE_DETAILS, +#ifdef SMALL_DOS + "%s%s%,13ld%-Fs", +#else + "%s%s%,13ld%-s", +#endif + psess->achMsg,achAttr,pfdin->cb,pfdin->psz1); + printf("%s\n",psess->achLine); + + psess->cFiles++; + psess->cbTotalBytes += pfdin->cb; + return 0; // Skip file, do not copy + + case fdintPARTIAL_FILE: + //** Construct output filespec +#ifdef SMALL_DOS + _fstrcpy(szLocal,pfdin->psz1); +#else +#define szLocal pfdin->psz1 +#endif + + //** See if filspec matches specified patterns + if (!checkWildMatches(psess,szLocal,perr)) { + //** Either no match, or failure -- figure out which + if (ErrIsError(perr)) { + return -1; // Error, abort + } + else { + return 0; // No error, skip this file + } + } + + //** Only show partial file messages for first cabinet + if (!psess->fContinuationCabinet) { // First cabinet + MsgSet(psess->achMsg,pszEXT_PARTIAL_FILE, +#ifdef SMALL_DOS + "%Fs%Fs%Fs", +#else + "%s%s%s", +#endif + pfdin->psz1,pfdin->psz2,pfdin->psz3); + printf("%s\n",psess->achMsg); + } + return 0; // Continue + + case fdintNEXT_CABINET: + return doGetNextCab(fdint,pfdin); + + default: + printf("UNKNOWN NOTIFICATION: %d\n",fdint); + return 0; /* ??? */ + } +} /* fdiNotifyDir() */ + + +/*** fdiNotifyExt - Callback from FDICopy for file extraction + * + * <<< Extract files! >>> + * + * Entry: + * fdint - type of notification + * pfdin - data for notification + * + * Exit-Success: + * Return value varies (see FDI.H:PFNFDINOTIFY type) + * + * Exit-Failure: + * Return value varies (see FDI.H:PFNFDINOTIFY type) + */ +FNFDINOTIFY(fdiNotifyExt) +{ + int fh; + FILETIMEATTR fta; + PERROR perr; + char *pszDestinationFile; + int rc; +#ifdef SMALL_DOS + PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv; + char szLocal[cbFILE_NAME_MAX]; +#else + PSESSION psess=(PSESSION)pfdin->pv; +#endif + + AssertSess(psess); + perr = psess->perr; + + //** Reset the spill file error code; + // We know that FDI is OK right now if it is asking us if we want + // to extract this file, so reset the spill file error code. If + // we did not, then it may have seNOT_ENOUGH_MEMORY (for example) + // as a result of Quantum trying to eat up all available memory, + // and a real decompression failure would be reported as an out + // of memory problem. + psess->se = seNONE; + + switch (fdint) { + case fdintCABINET_INFO: + return updateCabinetInfo(psess,pfdin); + + case fdintCOPY_FILE: + //** Construct output filespec +#ifdef SMALL_DOS + _fstrcpy(szLocal,pfdin->psz1); +#else +#define szLocal pfdin->psz1 +#endif + //** See if filspec matches specified patterns + if (!checkWildMatches(psess,szLocal,perr)) { + //** Either no match, or failure -- figure out which + if (ErrIsError(perr)) { + return -1; // Error, abort + } + else { + return 0; // No error, skip this file + } + } + + //** Figure out what destination file name should be + if (psess->achDest[0] != '\0') { // Override name from cabinet + pszDestinationFile = psess->achDest; + } + else { + pszDestinationFile = szLocal; + } + + //** Construct full destination file name + if (!catDirAndFile(psess->achFile, // Buffer for output filespec + sizeof(psess->achFile), // Size of output buffer + psess->achLocation, // Output directory + pszDestinationFile, // Output file name + NULL, // Don't have alternate name + perr)) { + return -1; // Abort with error; + } + + //** Make sure output directory exists + if (!ensureDirectory(psess->achFile,TRUE,perr)) { + return -1; // perr already filled in + } + + //** Do overwrite processing + if (!checkOverwrite(psess,psess->achFile,perr,&rc)) { + return rc; // Either Skip or Abort + } + + //** Create file + fh = wrap_open(psess->achFile, + _O_BINARY | _O_RDWR | _O_CREAT, // No translation, R/W + _S_IREAD | _S_IWRITE); // Attributes when file is closed + if (fh == -1) { + ErrSet(psess->perr,pszEXTERR_CANNOT_CREATE_FILE,"%s",psess->achFile); + return -1; // Failure + } + + //** Truncate file (in case it already existed and was larger) + if (0 != _chsize(fh, 0)) { + //** Not the best error, but avoids more localization! + ErrSet(psess->perr,pszEXTERR_CANNOT_CREATE_FILE,"%s",psess->achFile); + wrap_close(fh); + } + + //** Show status + if (pszDestinationFile == szLocal) { // File name is not changed + MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE,"%s",psess->achFile); + } + else { // Destination file is different + MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE2,"%s%s", + szLocal,psess->achFile); + } + + printf("%s\n",psess->achMsg); + psess->fNoLineFeed = TRUE; + psess->cFiles++; + psess->cbTotalBytes += pfdin->cb; + return fh; // Return open file handle + + case fdintCLOSE_FILE_INFO: + //** Close the file + wrap_close(pfdin->hf); + + //** Construct output filespec +#ifdef SMALL_DOS + _fstrcpy(szLocal,pfdin->psz1); +#else +#define szLocal pfdin->psz1 +#endif + + //** Figure out what destination file name should be + if (psess->achDest[0] != '\0') { // Override name from cabinet + pszDestinationFile = psess->achDest; + } + else { + pszDestinationFile = szLocal; + } + + //** Construct full destination file name + if (!catDirAndFile(psess->achFile, // Buffer for output filespec + sizeof(psess->achFile), // Size of output buffer + psess->achLocation, // Output directory + pszDestinationFile, // Output file name + NULL, // Don't have alternate name + perr)) { + return -1; // Abort with error + } + + + //** Set file date, time, and attributes + fta.date = pfdin->date; + fta.time = pfdin->time; + fta.attr = pfdin->attribs; + if (!SetFileTimeAndAttr(psess->achFile, &fta, perr)) { + return -1; // Abort with error + } + return TRUE; // Success + + case fdintPARTIAL_FILE: + //** Construct output filespec +#ifdef SMALL_DOS + _fstrcpy(szLocal,pfdin->psz1); +#else +#define szLocal pfdin->psz1 +#endif + //** See if filspec matches specified patterns + if (!checkWildMatches(psess,szLocal,perr)) { + //** Either no match, or failure -- figure out which + if (ErrIsError(perr)) { + return -1; // Error, abort + } + else { + return 0; // No error, skip this file + } + } + + //** Only show partial file messages for first cabinet + if (!psess->fContinuationCabinet) { // First cabinet + MsgSet(psess->achMsg,pszEXT_PARTIAL_FILE, +#ifdef SMALL_DOS + "%Fs%Fs%Fs", +#else + "%s%s%s", +#endif + pfdin->psz1,pfdin->psz2,pfdin->psz3); + printf("%s\n",psess->achMsg); + } + return 0; // Continue + + case fdintNEXT_CABINET: + return doGetNextCab(fdint,pfdin); + + default: + printf("UNKNOWN NOTIFICATION: %d\n",fdint); + return 0; /* ??? */ + } +} /* fdiNotifyExt() */ + + +/*** checkOverwrite - Check for file existence and do overwrite processing + * + * Entry: + * psess - Session + * pszFile - File to check + * perr - Error structure + * prc - Gets return code + * + * Exit-Success: + * Returns TRUE; file can be overwritten + * + * Exit-Failure: + * Returns FALSE; perr filled in if error, + * *prc == 0 -> Skip file + * *prc == -1 -> Abort + */ +BOOL checkOverwrite(PSESSION psess, + char *pszFile, + PERROR perr, + int *prc) +{ + char ch; + BOOL fGotReply; + BOOL fOverwrite; + BOOL fOverwriteAll; + struct _stat stat; + + //** Check to see if file already exists + if (-1 == _stat(pszFile,&stat)) { // File does not exist + return TRUE; // Write it + } + + //** Prompt if we're supposed to + if (!psess->fOverwrite) { + //** Display prompt -- no CR/LF + MsgSet(psess->achMsg,pszEXT_OVERWRITE_PROMPT,"%s",pszFile); + printf("%s",psess->achMsg); + + //** Get valid single character response and ENTER key; + // any illegal keys are just ignored + fGotReply = FALSE; + while (!fGotReply || (ch != '\r')) { + ch = _getch(); // Get a keystroke + switch (toupper(ch)) { + + case chOVERWRITE_YES: + fGotReply = TRUE; + fOverwrite = TRUE; + fOverwriteAll = FALSE; + printf("%c\b",ch); // Echo character and backspace over it + break; + + case chOVERWRITE_NO: + fGotReply = TRUE; + fOverwrite = FALSE; + fOverwriteAll = FALSE; + printf("%c\b",ch); // Echo character and backspace over it + break; + + case chOVERWRITE_ALL: + fGotReply = TRUE; + fOverwrite = TRUE; + fOverwriteAll = TRUE; + printf("%c\b",ch); // Echo character and backspace over it + break; + + default: + break; // Ignore character + } + } + + //** Do the line feed + printf("\n"); + + //** Respect user's wish + if (!fOverwrite) { // Don't overwrite file + *prc = 0; // Indicate skip + return FALSE; + } + else { // Overwrite once or all + psess->fOverwrite = fOverwriteAll; // Set accordingly + } + } + + //** Make sure file is writeable, if it isn't already + if (!(stat.st_mode & _S_IWRITE)) { // File is not writeable + _chmod(pszFile, _S_IREAD | _S_IWRITE); + //** Ignore error code, because the open will fail and catch it + } + + //** Done + return TRUE; // Overwrite that file +} /* checkOverwrite() */ + + +/*** updateCabinetInfo - update history of cabinets seen + * + * Entry: + * psess - Session + * pfdin - FDI info structurue + * + * Exit: + * Returns 0; + */ +int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin) +{ + AssertSess(psess); + + //** Save older cabinet info + psess->acab[0] = psess->acab[1]; + + //** Save new cabinet info + STRCPY(psess->acab[1].achCabPath ,pfdin->psz3); + STRCPY(psess->acab[1].achCabFilename ,pfdin->psz1); + STRCPY(psess->acab[1].achDiskName ,pfdin->psz2); + psess->acab[1].setID = pfdin->setID; + psess->acab[1].iCabinet = pfdin->iCabinet; + return 0; +} + + +/*** ensureCabinet - Make sure desired cabinet is available + * + * Make sure requested cabinet is available. + * + * Entry: + * psess - Session + * pszPath - Path buffer (modified if necessary on output) + * cbPath - Size of path buffer + * pszFile - Cabinet file name + * pszLabel - Label for disk with cabinet file + * setID - setID for cabinet + * iCabinet - iCabinet for cabinet + * fLoop - TRUE => Loop until right cabinet or user aborts + * FALSE => Only try once + * fPromptOnly - TRUE => Caller knows cabinet is bad, just prompt + * FALSE => Check cabinet, prompt if necessary + * perr - Error structure + * + * Exit-Success: + * Returns TRUE; desired cabinet is present + * + * Exit-Failure: + * Returns FALSE; perr filled in. + * Returns -1; user aborted + */ +BOOL ensureCabinet(PSESSION psess, + char *pszPath, + int cbPath, + char *pszFile, + char *pszLabel, + USHORT setID, + USHORT iCabinet, + BOOL fLoop, + BOOL fPromptOnly, + PERROR perr) +{ + char ach[cbFILE_NAME_MAX]; + int cch; + int cLoop=0; + char chDrive; + BOOL fCabinetExists; + FDICABINETINFO fdici; + int hfCab; + + AssertSess(psess); + + do { + cLoop++; // Count loops + ErrClear(perr); // Make sure no error is set + //** Construct fully qualified cabinet file path + if (!catDirAndFile(ach, // Buffer for output filespec + sizeof(ach), // Size of output buffer + pszPath, // Path + pszFile, // Filename + NULL, // Don't have alternate name + perr)) { + return FALSE; // Abort with error + } + + //** Check cabinet only if asked + if (!fPromptOnly) { + //** Make sure cabinet is the one we want + if (-1 == (hfCab = wrap_open(ach,_O_BINARY | _O_RDONLY,0))) { + ErrSet(perr,pszEXTERR_CANNOT_OPEN_FILE,"%s",ach); + } + else if (!FDIIsCabinet(psess->hfdi,hfCab,&fdici)) { + if (!ErrIsError(perr)) { // Have to set error message + ErrSet(perr,pszEXTERR_NOT_A_CABINET,"%s",ach); + } + } + else if ((fdici.setID != setID) || + (fdici.iCabinet != iCabinet)) { + ErrSet(perr,pszFDIERR_WRONG_CABINET,"%s",ach); + } + + //** Close the cabinet file (if we got it opened) + if (hfCab != -1) { + wrap_close(hfCab); + fCabinetExists = TRUE; + } + else { + fCabinetExists = FALSE; + } + + //** Did we get the cabinet we wanted? + if (!ErrIsError(perr)) { + return TRUE; // Yup, return success + } + + //** Don't show message if first time and cabinet not there, + // since this is the common case cabinets on separate floppy + // disks, and we don't want to whine before we ask them to + // insert the right floppy. + // + if ((cLoop > 1) || fCabinetExists) { + MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach); + printf("\n%s\n",psess->achMsg); + } + } + + //** Tell user what we want + if (IsPathRemovable(ach,&chDrive)) { + MsgSet(psess->achMsg,pszEXT_FLOPPY_PROMPT,"%s%s%c", + pszFile,pszLabel,chDrive); + } + else { + MsgSet(psess->achMsg,pszEXT_NOFLOPPY_PROMPT,"%s%s%c", + pszFile,pszLabel); + } + printf("%s\n",psess->achMsg); + + //** Get response + if (!gets(ach)) { // Error or EOF + ErrSet(perr,pszEXTERR_ABORT); + return -1; // User abort + } + if (strlen(ach) > 0) { + strcpy(pszPath,ach); // Update path + cch = strlen(pszPath); + //** Make sure path has path separator, since FDI requires it + cch += appendPathSeparator(&(pszPath[cch-1])); + + //** Update path for next FDICopy() call! + if (cch >= sizeof(psess->achCabPath)) { + Assert(0); + return -1; // Path too big + } + strcpy(psess->achCabPath,pszPath); + } + } + while (fLoop); + //** Did not guarantee desired cabinet + return FALSE; +} /* ensureCabinet() */ + + +/*** doGetNextCab - Get next cabinet + * + * Make sure requested cabinet is available. + * + * Entry: + * fdint - type of notification + * pfdin - data for notification + * + * Exit-Success: + * Returns anything but -1; + * + * Exit-Failure: + * Returns -1 => abort FDICopy() call. + */ +FNFDINOTIFY(doGetNextCab) +{ + char ach[cbFILE_NAME_MAX]; + static int cErrors=0; // Count of errors for single attempt + PERROR perr; + int rc; + +#ifdef SMALL_DOS + PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv; + static char szCabPath[cbFILE_NAME_MAX]; + static char szCabFile[cbFILE_NAME_MAX]; + static char szCabLabel[cbFILE_NAME_MAX]; +#else + PSESSION psess=(PSESSION)pfdin->pv; +#endif + + AssertSess(psess); + perr = psess->perr; + + //** Skip "starts in ..." messages for subsequent cabinets + psess->fContinuationCabinet = TRUE; + + //** Keep track of GetNextCabinet calls so we can determine + // what cabinet to expect next. + psess->fNextCabCalled = TRUE; + + //** If there is no problem to report, just let FDI do the checks + if (pfdin->fdie == FDIERROR_NONE) { + cErrors = 0; + return 0; + } + + //** If FDI didn't get the correct cabinet last time it called us, + // it calls us again with a specific error code. Tell the user + // something intelligible. + // + // pfdin->psz1 = cabinet filename + // pfdin->psz2 = disk user-readable name + // pfdin->psz3 = current cabinet path + +#ifdef SMALL_DOS + _fstrcpy(szCabFile ,pfdin->psz1); + _fstrcpy(szCabLabel,pfdin->psz2); + _fstrcpy(szCabPath ,pfdin->psz3); +#else +#define szCabFile pfdin->psz1 +#define szCabLabel pfdin->psz2 +#define szCabPath pfdin->psz3 +#endif + + cErrors++; // Count of errors on this cabinet + switch (pfdin->fdie) { + case FDIERROR_USER_ABORT: + Assert(0); //** Should never be called with this error code + break; + + default: + //** Construct full path name of cabinet + if (!catDirAndFile(ach, // Buffer for output filespec + sizeof(ach), // Size of output buffer + szCabPath, // Path + szCabLabel, // Filename + NULL, // Don't have alternate name + perr)) { + return -1; // Abort with error + } + //** Construct error string + mapFDIError(perr,psess,ach,&(psess->erf)); + //** Reset error + psess->erf.erfOper = FDIERROR_NONE; + } /* switch */ + + //** Tell user what the problem is, except in the case where the + // file was not found *and* this was the first try at finding + // the cabinet. + if ((cErrors > 1) || (pfdin->fdie != FDIERROR_CABINET_NOT_FOUND)) { + MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach); + printf("\n%s\n",psess->achMsg); + } + + //** Tell user to swap disks or type in new path + rc = ensureCabinet(psess, + szCabPath, // cabinet path + cbFILE_NAME_MAX, + szCabFile, // cabinet file name + szCabLabel, // User-readable label + pfdin->setID, // Required setID + pfdin->iCabinet, // Required iCabinet + FALSE, // Do not loop + TRUE, // Skip check, just prompt + perr); + +#ifdef SMALL_DOS + //** Copy possibly modified cabinet path back to FDI structure + _fstrcpy(pfdin->psz3,szCabPath); +#endif + + //** Return result + return rc; +} /* doGetNextCab() */ + + +/*** fdiDecryptDir - Callback from FDICopy for decryption + * + * <<< Just indicate calls made >>> + * + * NOTE: See fdi.h for details. + * + * Entry: + * pfdid - data for decryption + * + * Exit-Success: + * Return TRUE; + * + * Exit-Failure: + * Return -1; + */ +FNFDIDECRYPT(fdiDecryptDir) +{ + PERROR perr; +#ifdef SMALL_DOS + PSESSION psess=(PSESSION)(void *)(short)(long)pfdid->pvUser; +#else + PSESSION psess=(PSESSION)pfdid->pvUser; +#endif + + AssertSess(psess); + perr = psess->perr; + + //** Bail out if we're not supposed to show info + if (!psess->fShowReserveInfo) { + return TRUE; + } + + switch (pfdid->fdidt) { + case fdidtNEW_CABINET: + MsgSet(psess->achMsg,pszEXT_DECRYPT_HEADER, + "%08lx%u%x%d", + pfdid->cabinet.pHeaderReserve, + pfdid->cabinet.cbHeaderReserve, + pfdid->cabinet.setID, + pfdid->cabinet.iCabinet); + printf("%s\n",psess->achMsg); + break; + + case fdidtNEW_FOLDER: + MsgSet(psess->achMsg,pszEXT_DECRYPT_FOLDER, + "%08lx%u%d", + pfdid->folder.pFolderReserve, + pfdid->folder.cbFolderReserve, + pfdid->folder.iFolder); + printf("%s\n",psess->achMsg); + break; + + case fdidtDECRYPT: + MsgSet(psess->achMsg,pszEXT_DECRYPT_DATA, + "%08lx%u%08lx%u%d%u", + pfdid->decrypt.pDataReserve, + pfdid->decrypt.cbDataReserve, + pfdid->decrypt.pbData, + pfdid->decrypt.cbData, + pfdid->decrypt.fSplit, + pfdid->decrypt.cbPartial); + printf("%s\n",psess->achMsg); + break; + + default: + printf("UNKNOWN DECRYPT COMMAND: %d\n",pfdid->fdidt); + return -1; // Abort + }; + return TRUE; +} /* fdiDecryptDir() */ + + +/*** fdiDecryptExt - Callback from FDICopy for real decryption + * + * NOTE: See fdi.h for details. + * + * Entry: + * pfdid - data for decryption + * + * Exit-Success: + * Return TRUE; + * + * Exit-Failure: + * Return -1; + */ +FNFDIDECRYPT(fdiDecryptExt) +{ + return fdiDecryptDir(pfdid); +} /* fdiDecryptExt() */ + + +/*** checkWildMatchs - Check filespec against list of filespec patterns + * + * Entry: + * psess - SESSION -- has list of filespec patterns + * pszFile - Filespec to test (may have path characters) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, pszFile matched a pattern + * + * Exit-Failure: + * Returns FALSE, pszFile did not match a pattern -or- an error occurred. + * Use ErrIsError(perr) to determine if an error occured. + */ +BOOL checkWildMatches(PSESSION psess, char *pszFile, PERROR perr) +{ + HFILESPEC hfspec; // Use to walk list of file patterns + char *pszNameExt; // Name.Ext piece + char *pszWild; // Filespec pattern + + //** Get name.ext piece + pszNameExt = getJustFileNameAndExt(pszFile,perr); + if (!pszNameExt) { + return FALSE; // perr already filled in + } + + //** Loop through list of filespec patterns + hfspec = FLFirstFile(psess->hflist); + Assert(hfspec != NULL); // Skip over cabinet filespec + hfspec = FLNextFile(hfspec); + Assert(hfspec != NULL); // First wildcard spec + while (hfspec != NULL) { // Check patterns + pszWild = FLGetSource(hfspec); + Assert(pszWild!=NULL); + Assert(*pszWild); + if (IsWildMatch(pszNameExt,pszWild,perr)) { + return TRUE; // Got a match! + } + hfspec = FLNextFile(hfspec); // Try next pattern + } + + //** Failure -- none of the patterns matched + return FALSE; +} /* checkWildMatches() */ + + +/*** fdiAlloc - memory allocator for FDI + * + * Entry: + * cb - size of block to allocate + * + * Exit-Success: + * returns non-NULL pointer to block of size at least cb. + * + * Exit-Failure: + * returns NULL + */ +FNALLOC(fdiAlloc) +{ + void HUGE *pv; + + //** Do allocation +#ifdef BIT16 + pv = _halloc(cb,1); // Use 16-bit function +#else // !BIT16 + pv = malloc(cb); // Use 32-bit function +#endif // !BIT16 + + //** Remember if error occured, to improve quality of error messages + if (pv == NULL) { + psessG->se = seNOT_ENOUGH_MEMORY; + } + + //** Return buffer (or failure indication) + return pv; +} /* fdiAlloc() */ + + +/*** fdiFree - memory free function for FDI + * + * Entry: + * pv - memory allocated by fciAlloc to be freed + * + * Exit: + * Frees memory + */ +FNFREE(fdiFree) +{ +#ifdef BIT16 + //** Use 16-bit function + _hfree(pv); +#else // !BIT16 + //** Use 32-bit function + free(pv); +#endif // !BIT16 +} + + +/*** STATELOC - States for /L (location) parsing + * + */ +typedef enum { + slNONE, // No /L seen + slEXPECTING, // Just saw /L, need a location + slGOT, // We have parsed "/L location" +} STATELOC; /* sl */ + + +/*** parseCommandLine - Parse the command line arguments + * + * Entry: + * psess - SESSION + * cArg - Count of arguments, including program name + * apszArg - Array of argument strings + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, psess filled in. + * + * Exit-Failure: + * Returns actBAD, perr filled in with error. + */ +BOOL parseCommandLine(PSESSION psess, int cArg, char *apszArg[], PERROR perr) +{ + int cFile=0; // Count of non-directive file names seen + char ch; // Switch value + int i; + char *pch; + STATELOC sl=slNONE; // Location parsing state + + AssertSess(psess); + psess->act = actDEFAULT; // We don't know what we are doing; yet + psess->achLocation[0] = '\0'; // Default to current directory + + //** Empty file handle table + for (i=0; i<cMAX_CAB_FILE_OPEN; i++) { + psess->ahfSelf[i] = -1; // No open handle + } + + //** See if we are a self-extracting cabinet + if (!checkSelfExtractingCab(psess,cArg,apszArg,perr)) { + return FALSE; // An error occurred, perr filled in + } + if (psess->fSelfExtract) { + //** Save our file name and use it as the cabinet file name + strcpy(psess->achSelf,apszArg[0]); // Save our name + if (addFileSpec(psess,apszArg[0],perr) == NULL) { + ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",apszArg[0]); + return FALSE; + } + cFile++; // Count files + + } + + //** Parse args, skipping program name + for (i=1; i<cArg; i++) { + if ((apszArg[i][0] == chSWITCH1) || + (apszArg[i][0] == chSWITCH2) ) { + //** Have a switch to parse, make sure switch is OK here + if (sl == slEXPECTING) { + ErrSet(perr,pszEXTERR_MISSING_LOCATION); + return FALSE; + } + + //** Process switches (support string to ease typing) + for (pch=&apszArg[i][1]; *pch; pch++) { + ch = toupper(*pch); // Switch character + switch (ch) { + case chSWITCH_HELP: + psess->act = actHELP; // Show help + return TRUE; + + case chSWITCH_ALL: + psess->fAllCabinets = TRUE; + break; + + case chSWITCH_COPY: + if (psess->act != actDEFAULT) { + ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch); + return FALSE; + } + psess->act = actCOPY; + break; + + case chSWITCH_DIRECTORY: + if (psess->act != actDEFAULT) { + ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch); + return FALSE; + } + psess->act = actDIRECTORY; + break; + + case chSWITCH_EXTRACT: + if (psess->act != actDEFAULT) { + ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch); + return FALSE; + } + psess->act = actEXTRACT; + break; + + case chSWITCH_LOCATION: + //** Make sure we only got location once + if (sl == slGOT) { + ErrSet(perr,pszEXTERR_LOCATION_TWICE); + return FALSE; + } + sl = slEXPECTING; + break; + + case chSWITCH_OVERWRITE: + psess->fOverwrite = TRUE; + break; + + case chSWITCH_RESERVE: + psess->fShowReserveInfo = TRUE; + break; + + default: + ErrSet(perr,pszEXTERR_BAD_SWITCH,"%s",apszArg[i]); + return FALSE; + break; + } + } + } + //** Not a command line switch + else if (sl == slEXPECTING) { + //** Get the location (output directory) + STRCPY(psess->achLocation,apszArg[i]); // Save location + sl = slGOT; // Done eating location + } + else { + //** We have a file name, add it to our list + if (addFileSpec(psess,apszArg[i],perr) == NULL) { + ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",apszArg[i]); + return FALSE; + } + cFile++; // Count files + } + } + + //** If no arguments and not self-extracting, show help + if ((cArg == 1) && !psess->fSelfExtract) { + psess->act = actHELP; // Show help + return TRUE; + } + + //** Make sure no trailing /L without location + if (sl == slEXPECTING) { + ErrSet(perr,pszEXTERR_MISSING_LOCATION); + return FALSE; + } + + //** Make sure we got right number of arguments for COPY case + if ((psess->act == actCOPY) && (cFile != 2)) { + //** General purpose error, to minimize localization effort + ErrSet(perr,pszEXTERR_BAD_PARAMETERS); + return FALSE; + } + + //** Make sure we got at least one filespec + if (cFile == 0) { + ErrSet(perr,pszEXTERR_MISSING_CABINET); + return FALSE; + } + + //** Special processing for self-extract + if (psess->fSelfExtract) { + psess->fAllCabinets = TRUE; // Always do all cabinets + //** Force EXTRACT if no /E or /D specified + if (psess->act == actDEFAULT) { + psess->act = actEXTRACT; + } + } + + //** Success + return TRUE; +} + +/*** checkSelfExtractingCab - See if we are a self-extracting cabinet + * + * Entry: + * psess - SESSION + * cArg - Count of arguments, including program name + * apszArg - Array of argument strings + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, psess filled in. + * psess->fSelfExtract set to TRUE if self-extracting + * psess->cbSelfExtract set to EXE size if self-extracting (this is + * the offset to the start of the cabinet file header). + * + * Exit-Failure: + * Returns actBAD, perr filled in with error. + * + * Notes: + * The strategy is to get the EXE file size indicated in the appropriate + * EXE header, and if the actual size of our EXE file is larger than that, + * we assume that there is a cabinet file tacked on to the end, and we + * extract the files from that! + */ +BOOL checkSelfExtractingCab(PSESSION psess, + int cArg, + char *apszArg[], + PERROR perr) +{ + long cbFile; // EXE file size + long cbFromHeader; // Size indicated by EXE header + int hf; +#ifdef BIT16 + long info; +#else // !BIT16 + char achFile[cbFILE_NAME_MAX]; + IMAGE_DOS_HEADER idh; // MS-DOS header + IMAGE_NT_HEADERS inh; // Complete PE header +#endif // !BIT16 + + + //** Open our EXE file +#ifdef BIT16 + // printf("opening EXE file: %s\n",apszArg[0]); + hf = wrap_open(apszArg[0],_O_RDONLY,0); +#else + // printf("Geting EXE file name\n"); + if (!GetModuleFileName(NULL,achFile,sizeof(achFile))) { + return TRUE; + } + // printf("opening EXE file: %s\n",achFile); + hf = wrap_open(achFile,_O_RDONLY,0); +#endif + if (hf == -1) { + return TRUE; // Something is bogus, just skip selfex + } + + //** Get the expected EXE file size +#ifdef BIT16 + //** Do it the MS-DOS way + // printf("seek to EXE size\n"); + if (-1 == wrap_lseek(hf,2,SEEK_SET)) { + goto Exit; + } + // printf("reading EXE size\n"); + if (sizeof(info) != wrap_read(hf,&info,sizeof(info))) { + goto Exit; + } + //** OK, we've got the page count and count of bytes in the last page; + // convert to a file size. + cbFromHeader = ((info>>16)-1)*512 + (info&0xFFFF); + // printf("Original EXE size = %8ld\n",cbFromHeader); +#else // !BIT16 + //** Do it the Win32 way + + // printf("reading MS-DOS header\n"); + //** Get MS-DOS header + if (sizeof(idh) != wrap_read(hf,&idh,sizeof(idh))) { + goto Exit; + } + + // printf("seeking to NT header at %ld\n",idh.e_lfanew); + //** Seek to and read NT header + if (-1 == wrap_lseek(hf,idh.e_lfanew,SEEK_SET)) { + goto Exit; + } + + // printf("reading NT header\n"); + if (sizeof(inh) != wrap_read(hf,&inh,sizeof(inh))) { + goto Exit; + } + + cbFromHeader = inh.OptionalHeader.SizeOfImage; + // printf("Original EXE size = %8ld\n",inh.OptionalHeader.SizeOfImage); +#endif // !BIT16 + + //** Get actual file size + cbFile = wrap_lseek(hf,0,SEEK_END); + // printf("Current EXE size = %8ld\n",cbFile); + + //** Modify state IF we are doing self-extract + if (cbFile > cbFromHeader) { + psess->fSelfExtract = TRUE; + psess->cbSelfExtract = cbFromHeader; + } + + //** Success +Exit: + wrap_close(hf); + return TRUE; +} /* checkSelfExtractingCab() */ + + + +/*** addFileSpec - Add filename to session list + * + * Entry: + * psess - Session to update + * pszArg - File name to add + * perr - ERROR structure + * + * Exit-Success: + * Returns HFILESPEC, psess updated. + * + * Exit-Failure: + * Returns NULL, perr filled in with error. + */ +HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr) +{ + HFILESPEC hfspec; + + AssertSess(psess); + //** Make sure a list exists + if (psess->hflist == NULL) { + if (!(psess->hflist = FLCreateList(perr))) { + return FALSE; + } + } + + //** Add file to list + if (!(hfspec = FLAddFile(psess->hflist, pszArg, NULL, perr))) { + return NULL; + } + + //** Success + return hfspec; +} /* addFileSpec() */ + + +#ifdef ASSERT +/*** fnafReport - Report assertion failure + * + * NOTE: See asrt.h for entry/exit conditions. + */ +FNASSERTFAILURE(fnafReport) +{ + printf("\n%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg); + exit(1); +} +#endif // ASSERT + + +/*** printError - Display error on stdout + * + * Entry + * perr - ERROR structure to print + * + * Exit-Success + * Writes error message to stdout. + */ +void printError(PSESSION psess, PERROR perr) +{ + //** Make sure error starts on a new line + if (psess->fNoLineFeed) { + printf("\n"); + psess->fNoLineFeed = FALSE; + } + + //** General error + Assert(perr->pszFile == NULL); + MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach); + printf("%s\n",psess->achMsg); +} /* printError() */ + + +/*** pszFromMSDOSTime - Convert MS-DOS file date/time to string + * + * Entry: + * psz - Buffer to receive formatted date/time + * cb - Length of psz buffer + * date - MS-DOS FAT file system date format (see below) + * time - MS-DOS FAT file system time format (see below) + * + * Exit: + * *psz filled in + * + * NOTE: This is the interpretation of the MS-DOS date/time values: + * + * Time Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Number of two-second increments (0 - 29) + * 5 - 10 6 Minutes (0 - 59) + * 11 - 15 5 Hours (0 - 23) + * + * Date Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Day (1 - 31) + * 5 - 8 4 Month (1 - 12) + * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14) + */ +void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time) +{ + int sec; + int min; + int hour; + int day; + int month; + int year; + char *pszAMPM; // AM/PM string + + sec = (time & 0x1f) << 1; // 0, 2, ..., 58 + min = (time >> 5) & 0x3f; // 0, 1, ..., 59 + hour = (time >> 11) & 0x1f; // 0, 1, ..., 23 + + //** Determine 12-hour vs. 24-hour clock + if (strlen(pszEXT_TIME_PM) == 0) { + //** 24 hour clock + Assert(strlen(pszEXT_TIME_AM) == 0); + pszAMPM = pszEXT_TIME_PM; + } + else { + //** Get am/pm extension, and map 0 to 12 + if (hour >= 12) { + pszAMPM = pszEXT_TIME_PM; + hour -= 12; + } + else { + pszAMPM = pszEXT_TIME_AM; + } + if (hour == 0) { + hour = 12; + } + } + + day = (date & 0x1f); + month = (date >> 5) & 0x0f; + year = ((date >> 9) & 0x7f) + 1980; + + MsgSet(psz,pszEXT_DATE_TIME, "%02d%02d%02d%2d%02d%02d%s", + month, day, year, hour, min, sec, pszAMPM); +} /* pszFromMSDOSTime() */ + + +/*** pszFromAttrFAT - Convert FAT file attributes to string + * + * Entry: + * attrFAT - file attributes + * + * Exit: + * *psz filled in "----".."A-R-".."AHRS" + */ +void pszFromAttrFAT(char *psz, int cb, WORD attrFAT) +{ + STRCPY(psz,"----"); + if (attrFAT & _A_ARCH) + psz[0] = 'A'; + if (attrFAT & _A_HIDDEN) + psz[1] = 'H'; + if (attrFAT & _A_RDONLY) + psz[2] = 'R'; + if (attrFAT & _A_SYSTEM) + psz[3] = 'S'; + return; +} /* pszFromAttrFAT() */ + + +/*** mapFDIError - Create error message from FDI error codes + * + * Entry: + * perr - ERROR structure to recieve message + * psess - Our context + * pszCabinet - Cabinet file being processed + * perf - FDI error structure + * + * Exit: + * perr filled in with formatted message + */ +void mapFDIError(PERROR perr,PSESSION psess, char *pszCabinet, PERF perf) +{ + switch (perf->erfOper) { + + case FDIERROR_NONE: + Assert(0); + break; + + case FDIERROR_CABINET_NOT_FOUND: + ErrSet(perr,pszFDIERR_CAB_NOT_FOUND,"%s",pszCabinet); + break; + + case FDIERROR_NOT_A_CABINET: + ErrSet(perr,pszFDIERR_NOT_A_CABINET,"%s",pszCabinet); + break; + + case FDIERROR_UNKNOWN_CABINET_VERSION: + ErrSet(perr,pszFDIERR_BAD_CAB_VER,"%s%04x",pszCabinet,perf->erfType); + break; + + case FDIERROR_CORRUPT_CABINET: + ErrSet(perr,pszFDIERR_CORRUPT_CAB,"%s",pszCabinet); + break; + + case FDIERROR_ALLOC_FAIL: + ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet); + break; + + case FDIERROR_BAD_COMPR_TYPE: + ErrSet(perr,pszFDIERR_BAD_COMPR_TYPE,"%s",pszCabinet); + break; + + case FDIERROR_MDI_FAIL: + //** Improve detail of failure message + + switch (psess->se) { + + case seNONE: + //** Some other decompression error (corrupted data?) + ErrSet(perr,pszFDIERR_MDI_FAIL,"%s",pszCabinet); + break; + + case seNOT_ENOUGH_MEMORY: + //** Not enough RAM for decompressor itself + ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet); + break; + + case seCANNOT_CREATE: + //** Could not create a Quantum temporary spill file + ErrSet(perr,pszFDIERR_SPILL_CREATE,"%s%s",pszCabinet,achSpillFile); + break; + + case seNOT_ENOUGH_SPACE: + //** TMP directory did not have enough space for Quantum + // spill file. + ErrSet(perr,pszFDIERR_SPILL_SIZE,"%s%s%ld",pszCabinet, + achSpillFile, + psess->cbSpill); + break; + + default: + Assert(0); + } + break; + + case FDIERROR_TARGET_FILE: + ErrSet(perr,pszFDIERR_TARGET_FILE,"%s%s",psess->achFile,pszCabinet); + break; + + case FDIERROR_RESERVE_MISMATCH: + ErrSet(perr,pszFDIERR_RESERVE_MISMATCH,"%s",pszCabinet); + break; + + case FDIERROR_WRONG_CABINET: + ErrSet(perr,pszFDIERR_WRONG_CABINET,"%s",pszCabinet); + break; + + case FDIERROR_USER_ABORT: + ErrSet(perr,pszFDIERR_USER_ABORT,"%s",pszCabinet); + break; + + default: + ErrSet(perr,pszFDIERR_UNKNOWN_ERROR,"%d%s",perf->erfOper,pszCabinet); + break; + } +} /* mapFDIError() */ + + +/*** wrap_close - close an open file + * + */ +int FAR DIAMONDAPI wrap_close(int fh) +{ + int i; + int rc; + +#ifdef SMALL_DOS + rc = _dos_close(fh); + if (rc != 0) { // Map random MS-DOS error code to -1 failure + rc = -1; + } +#else + rc = _close(fh); +#endif + + //** See if we have to destroy the spill file + if (fh == hfSpillFile) { + _unlink(achSpillFile); // Delete spill file + hfSpillFile = -1; // Remember spill file is gone + } + + //** Take handle off list if we are self-extracting + if (psessG->fSelfExtract) { + //** See if this is a handle to our EXE/cabinet file; + for (i=0; + (i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[i] != fh); + i++) { ; } + if (i < cMAX_CAB_FILE_OPEN) { // Found a match + psessG->ahfSelf[i] = -1; // Take it off our list + dbg( printf("\nDBG: Close self as handle %d (slot %d)\n",fh,i) ); + } + } + + //** Done + dbg( printf("DBG: %d=CLOSE on handle %d\n",rc,fh) ); + return rc; +} /* wrap_close */ + + +/*** wrap_lseek - seek on a file + * + */ +long FAR DIAMONDAPI wrap_lseek(int fh, long pos, int func) +{ + long cbAdjust=0; // Assume file is 0-based + int i; + long rc; + + //** See if we are self-extracting + if (psessG->fSelfExtract) { + //** See if this is a handle to our EXE/cabinet file; + for (i=0; + (i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[i] != fh); + i++) { ; } + if (i < cMAX_CAB_FILE_OPEN) { // Found a match + cbAdjust = psessG->cbSelfExtract; // So return value gets adjusted + if (func == SEEK_SET) { // Need to adjust absolute position + pos += cbAdjust; // Shift up to account for EXE + dbg(printf("\nDBG: Seek self to %ld as handle %d (slot %d)\n",pos,fh,i)); + } + } + } + +#ifdef SMALL_DOS + rc = _dos_seek(fh,pos,func); +#else + rc = _lseek(fh,pos,func); +#endif + //** If seek didn't fail, adjust return value for self-extract case + if (rc != -1) { + rc -= cbAdjust; + } + + dbg( printf("DBG: %ld=LSEEK on handle %d, pos=%ld, func=%d\n",rc,fh,pos,func) ); + return rc; +} /* wrap_lseek() */ + + +/*** wrap_open - open a file + * + */ +int FAR DIAMONDAPI wrap_open(const char FAR *sz, int mode, int share) +{ + int i; + int rc; + char FAR *psz; + PFDISPILLFILE pfdisf; // FDI spill file info +#ifdef SMALL_DOS + int ignore; + char szLocal[cbFILE_NAME_MAX]; +#endif + + //** See if FDI is asking for a spill file (for Quantum) + if (*sz == '*') { // Yes, we need to create a spill file + Assert(hfSpillFile == -1); // Only support one at a time + achSpillFile[0] = '\0'; // No name constructed, yet + pfdisf = (PFDISPILLFILE)sz; // Get pointer to spill file size + //** Try boot drive if no TEMP variable defined + // NOTE: We assume the boot drive is more likely to be writeable + // than the current directory, since the customer may be + // running EXTRACT.EXE off a write-protected floppy (which + // also wouldn't have enough space), or a read-only network + // drive. + psz = _tempnam(getBootDrive(),"esf"); // Get a temporary file name + if (psz == NULL) { + psessG->se = seCANNOT_CREATE; + return -1; // Could not create + } + strcpy(achSpillFile,psz); // Remember name for wrap_close + free(psz); // Free temporary name buffer + + mode = _O_CREAT | _O_BINARY | _O_RDWR; // Force open mode + psz = achSpillFile; // Use spill file name + } + else { + psz = (char FAR *)sz; // Use passed-in name + } + + //** Open/create file +#ifdef SMALL_DOS + _fstrcpy(szLocal,psz); + if (mode & _O_CREAT) { + ignore = _dos_creat(szLocal,_A_NORMAL,&rc); + } + else { + //** Keep only relevant bits for _dos_open! + mode &= _O_RDONLY | _O_WRONLY | _O_RDWR; + ignore = _dos_open(szLocal,mode,&rc); + } + if (ignore != 0) { + rc = -1; + } +#else + rc = _open(psz,mode,share); +#endif + + //** If this is the spill file, make sure the file was created, + // make sure it is the requested size, and save the handle. + // If we cannot do this, we set a flag to remember what the + // problem was, so that we can report the error intelligently. + if (*sz == '*') { // Need to size spill file + if (-1 == rc) { // Could not create it + psessG->se = seCANNOT_CREATE; + return rc; + } + //** Remember file handle, so that wrap_close can do the delete + hfSpillFile = rc; + + //** Don't need to seek/write if zero length requested + if (pfdisf->cbFile > 0) { + //** Seek to size minus 1 + if (-1L == wrap_lseek(rc,pfdisf->cbFile-1,SEEK_SET)) { + psessG->se = seNOT_ENOUGH_SPACE; + psessG->cbSpill = pfdisf->cbFile; + wrap_close(rc); // Close and destroy spill file + return -1; + } + + //** Write one byte + if (1 != wrap_write(rc,"b",1)) { + psessG->se = seNOT_ENOUGH_SPACE; + psessG->cbSpill = pfdisf->cbFile; + wrap_close(rc); + return -1; + } + } + //** Spill file created successfully + psessG->se = seNONE; // No error + } +#ifndef BIT16 +#define _f_stricmp(a,b) _stricmp(a,b) +#endif + else if (psessG->fSelfExtract && !_f_stricmp(sz,psessG->achSelf)) { + //** Self-extracting and this is our EXE/cabinet file; + // Find a slot to store the file handle. + for (i=0; + (i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[i] != -1); + i++) { ; } + if (i >= cMAX_CAB_FILE_OPEN) { + Assert(0); + wrap_close(rc); + return -1; + } + dbg( printf("\nDBG: Opened self (%s) as handle %d (slot %d)\n",sz,rc,i) ); + + //** Save the new handle + psessG->ahfSelf[i] = rc; + + //** Position the file handle to the start of the cabinet file header! + // NOTE: Since we just added the handle to the list, wrap_lseek() + // will know to do the EXE size adjustment! + wrap_lseek(rc,0,SEEK_SET); + } + + //** Done + dbg( printf("DBG: %d=OPEN file %s, mode=%d, share=%d\n",rc,sz,mode,share) ); + return rc; +} /* wrap_open() */ + + +/*** wrap_read - read a file + * + */ +UINT FAR DIAMONDAPI wrap_read(int fh, void FAR *pb, unsigned int cb) +{ + int rc; + +#ifdef SMALL_DOS + UINT ignore; + ignore = _dos_read(fh,pb,cb,&rc); + if (ignore != 0) { + rc = -1; + } +#else + rc = _read(fh,pb,cb); +#endif + dbg( printf("DBG: %d=READ on handle %d, pb=%08lx, cb=%u\n",rc,fh,pb,cb) ); + return rc; +} /* wrap_read() */ + + +/*** wrap_write - write a file + * + */ +UINT FAR DIAMONDAPI wrap_write(int fh, const void FAR *pb, unsigned int cb) +{ + int rc; + +#ifdef SMALL_DOS + UINT ignore; + ignore = _dos_write(fh,pb,cb,&rc); + if (ignore != 0) { + rc = -1; + } +#else + rc = _write(fh,pb,cb); +#endif + dbg( printf("DBG: %d=WRITE on handle %d, pb=%08lx, cb=%u\n",rc,fh,pb,cb) ); + return rc; +} /* wrap_write() */ + + +/*** getBootDrive - Returns boot drive path (e.g., "C:\") + * + * Entry: + * none + * + * Exit: + * Returns pointer to static buffer with bootdrive ("C:\") + */ +char *getBootDrive(void) +{ + char ch; + char *psz; + static char szBootDrive[]="C:\\"; + + //** Default to Drive C + *szBootDrive = 'C'; + + //** Get COMSPEC -- we're assuming it's drive letter is the boot drive! + psz = getenv("COMSPEC"); + if ( psz && // COMSPEC exists + *psz && // It is not empty + (*(psz+1) == ':')) { // Has the right format -- "?:..." + //** We could try to validate that this is really the boot drive, + // but we'll just trust that COMSPEC is correct. A test for the + // drive being in the range A..C would work for the US, but in + // Japan the boot drive can be anything between A..G, and maybe + // even higher. So, we'll just make sure it's a drive letter. + ch = tolower(*psz); + if (('a' <= ch) && (ch <= 'z')) { + *szBootDrive = ch; // Use COMSPEC drive letter + } + } + + //** Return path of root of boot drive + return szBootDrive; +} /* getBootDrive() */ + + +#ifdef BIT16 +//** Get Changeline fix code +//BUGBUG 14-Dec-1994 bens Include *.c file to avoid makefile change! +#include "fixchg.c" +#endif diff --git a/private/windows/diamond/extract.msg b/private/windows/diamond/extract.msg new file mode 100644 index 000000000..5f12a6060 --- /dev/null +++ b/private/windows/diamond/extract.msg @@ -0,0 +1,605 @@ +/*** extract.msg - EXTRACT.EXE displayable strings + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * History: + * 17-Feb-1994 bens Initial version + * 19-Feb-1994 bens Produce directory + * 28-Feb-1994 bens Update version + * 02-Mar-1994 bens Link with improved fdi.lib + * 08-Mar-1994 bens Add date/time display + * 17-Mar-1994 bens RESERVE support added + * 22-Mar-1994 bens Refresh with more robust FDI + * 25-Mar-1994 bens Handle FDIERROR_WRONG_CABINET + * 28-Mar-1994 bens Suport /A switch + * 01-Apr-1994 bens Add /D and /E switches + * 07-Apr-1994 bens Add decrypt support (at least for debugging) + * 18-Apr-1994 bens Pick up bug fix for incompressible data + * 27-May-1994 bens Quantum support + * 07-Jun-1994 bens Add localization comments (whew!) + * 24-Jun-1994 bens Don't show RESERVEd cabinet info + * 08-Jul-1994 bens Quantum spill file support + * 11-Jul-1994 bens Empty AM/MP strings => 24 hour time format + * 26-Jul-1994 bens Add /C switch + * 02-Aug-1994 bens Added notes for localizers + * 12-Mar-1995 bens Remove variable data from pszBANNER! + */ + +/*** NOTES FOR LOCALIZERS -- PLEASE READ! + * + * 1) Do not put TAB characters in strings -- they will throw off the + * spacing, especially in the /? help text. If you are using Word + * or some other non-programmer's editor, please DO NOT press the + * TAB key, and, just to be safe, turn on display of tabs and spaces + * to make sure you haven't added any TABs inadvertently. + * + * 2) Do not leave blanks at the end of lines, especially the continutation + * lines (ending with \). Again, turn on display of tabs and spaces + * (if your text editor supports that) to make sure -- the C compiler + * does not like these! + */ + + /*LOCALIZE + * + * Purpose: The name, date, and version of this tool: + * %1 = bitness (16 or 32) + * %2 = version string (n.nn.nnnn) + * %3 = no BIOS dependency indictor (n or empty) + * %4 = month (01..12) + * %5 = day (01..31) + * %6 = year (00..99) + * %7 = copyright years (1994-1995) + * Generate: Type "EXTRACT" + */ +#define pszBANNER \ + "Microsoft (R) Diamond Extraction Tool - Version (%1) %2%3 (%4/%5/%6)\n" \ + "Copyright (c) Microsoft Corp %7. All rights reserved.\n" + + + /*LOCALIZE + * + * Purpose: Command line syntax. + * Generate: Type "EXTRACT /?" or just "EXTRACT" + */ +#define pszCMD_LINE_HELP \ + "EXTRACT [/Y] [/A] [/D | /E] [/L dir] cabinet [filename ...]\n" \ + "EXTRACT [/Y] source [newname]\n" \ + "EXTRACT [/Y] /C source destination\n" \ + "\n" \ + " cabinet - Cabinet file (contains two or more files).\n" \ + " filename - Name of the file to extract from the cabinet.\n" \ + " Wild cards and multiple filenames (separated by\n" \ + " blanks) may be used.\n" \ + "\n" \ + " source - Compressed file (a cabinet with only one file).\n" \ + " newname - New filename to give the extracted file.\n" \ + " If not supplied, the original name is used.\n" \ + "\n" \ + " /A Process ALL cabinets. Follows cabinet chain\n" \ + " starting in first cabinet mentioned.\n" \ + " /C Copy source file to destination (to copy from DMF disks).\n" \ + " /D Display cabinet directory (use with filename to avoid extract).\n" \ + " /E Extract (use instead of *.* to extract all files).\n" \ + " /L dir Location to place extracted files (default is current directory).\n" \ + " /Y Do not prompt before overwriting an existing file." + +/* + ** Command Line Switches + */ + + /*NoLocalize + */ +#define chSWITCH1 '/' +#define chSWITCH2 '-' + + /*NoLocalize + */ +#define chSWITCH_HELP '?' +#define chSWITCH_ALL 'A' +#define chSWITCH_COPY 'C' +#define chSWITCH_DIRECTORY 'D' +#define chSWITCH_EXTRACT 'E' +#define chSWITCH_LOCATION 'L' +#define chSWITCH_RESERVE 'R' //** Purposely undocumented! +#define chSWITCH_OVERWRITE 'Y' + +/* + ** Status messages + */ + + /*LOCALIZE + * + * Purpose: A destination file already exists, ask if user wants to + * overwrite it. + * %1 = destination file name + * Generate: Type: + * EXTRACT loctest1.cab a.asc + * EXTRACT loctest1.cab a.asc + * Expect: "Overwrite a.asc (Yes/No/All)?" + * ^^^^^^^^^^-----^^^^^^^^^^^^^^ + */ +#define pszEXT_OVERWRITE_PROMPT "Overwrite %1 (Yes/No/All)?" +#define chOVERWRITE_YES 'Y' +#define chOVERWRITE_NO 'N' +#define chOVERWRITE_ALL 'A' + + + /*LOCALIZE + * + * Purpose: Indicates name of current cabinet file being processed. + * %1 = cabinet file name + * Generate: Type "EXTRACT /D loctest1.cab" + * Expect: " Cabinet loctest1.cb" + */ +#define pszEXT_CABINET_HEADER " Cabinet %1" + + + /*LOCALIZE + * + * Purpose: Indicates name of a file being extracted to current directory. + * %1 = file name from inside a cabinet file + * Generate: Type "EXTRACT loctest1.cab a.asc" + * Expect: "Extracting a.asc" + */ +#define pszEXT_EXTRACTING_FILE "Extracting %1" + + + /*LOCALIZE + * + * Purpose: Indicates file being extracted to a different directory. + * %1 = file name from inside a cabinet file + * %2 = path specified on /L parameter where file is going + * Generate: Type "EXTRACT /A /L raw loctest1.cab a.asc" + * Expect: "Extracting a.asc -> raw\a.asc" + * (assuming foo.cab contains file.exe) + */ +#define pszEXT_EXTRACTING_FILE2 "Extracting %1 -> %2" + + + /*LOCALIZE + * + * Purpose: Indicates that requested files were not found. + * Generate: Type "EXTRACT /D loctest1.cab missing.dat" + * Expect: "No matching files." + */ +#define pszEXT_NO_MATCHING_FILES "No matching files." + + + /*LOCALIZE + * + * Purpose: File date/time format (used on file directory lines): + * %1 = month (1..12) + * %2 = day (1..31) + * %3 = year (1..99) + * %4 = hour (1..12) + * %5 = minute (0..59) + * %6 = second (0..59) + * %7 = AM/PM indicator (see pszEXT_TIME_AM/PM) + * Generate: Type "EXTRACT /D loctest1.cab a.asc" + * Expect: "04-01-1994 3:28:08p A--- 16,184 a.asc" + * --^--^----^--^--^--- + * This is the portion produced from the following 3 strings + * + * NOTE: For German and other languages/countries that use a 24-hour + * clock (00:00:01 to 23:59:59), set the AM/PM strings to the empty + * string (i.e., ""), and that will force 24-hour format! + */ +#define pszEXT_DATE_TIME "%1-%2-%3 %4:%5:%6%7" +#define pszEXT_TIME_AM "a" +#define pszEXT_TIME_PM "p" + + + /*NoLocalize - (date/time attributes filesize filename) + */ +#define pszEXT_FILE_DETAILS "%1 %2 %3 %4" + + + /*LOCALIZE + * + * Purpose: Summary line with 1 file and total number of bytes: + * %1 = 1 + * %2 = count of bytes + * Generate: Type "EXTRACT /D loctest1.cab a.asc" + * Expect: " 1 File 16,184 bytes" + * ----^^^^^^^^^^^^^^-------^^^^^^ + */ +#define pszEXT_SUMMARY1 " %1 File %2 bytes" // 1 file + + + /*LOCALIZE + * + * Purpose: Summary line with >1 files and total number of bytes: + * %1 = count of files + * %2 = count of bytes + * Generate: Type "EXTRACT /D loctest1.cab" + * Expect: " 16 Files 258,944 bytes" + * ----^^^^^^^^^^^^^^-------^^^^^^ + */ +#define pszEXT_SUMMARY2 " %1 Files %2 bytes" // >1 file + + + /*LOCALIZE + * + * Purpose: Indicates a file that is split across a cabinet boundary. + * %1 = filename of file that is split + * %2 = cabinet file name where file starts + * %3 = Descriptive disk name where cabinet file exists + * Generate: Type "EXTRACT /D loctest2.cab m.asc" + * Expect: "m.asc : Starts in cabinet loctest1.cab on disk 'Disk 1'" + * -----^^^^^^^^^^^^^^^^^^^^^------------^^^^^^^^^^------^ + */ +#define pszEXT_PARTIAL_FILE "%1 : Starts in cabinet %2 on disk '%3'" + + + /*NoLocalize - This are informative displays on cabinets with RESERVED space + */ +//BUGBUG 07-Jun-1994 bens What do we do with this stuff? +// These were present for testing RESERVED stuff, in the absence of an +// actual crytposystem. +#define pszEXT_DECRYPT_HEADER "CRYPT Header: r=<%1,%2> setID=%3 iCabinet=%4" +#define pszEXT_DECRYPT_FOLDER "CRYPT Folder: r=<%1,%2> iFolder=%3" +#define pszEXT_DECRYPT_DATA "CRYPT Data: r=<%1,%2> b=<%3,%4> fSplit=%5 cbPartial=%6" + + + /*LOCALIZE + * + * Purpose: Prompt for next cabinet when previous cabinet was on a floppy. + * %1 = Name of desired cabinet file + * %2 = Disk label of floppy with desired cabinet + * %3 = Drive letter of floppy drive + * Generate: Copy loctest1.cab to one disk, and loctest2.cab to another + * disk, then stick the first disk in drive A and type + * "EXTRACT loctest1.cab m.asc" + * Expect: + * "Extract needs cabinet file 'loctest2.cab'. Please insert the disk" + * "labeled 'Disk 2' into drive A, and press ENTER. You may + * "enter a different drive and/or path:" + */ +#define pszEXT_FLOPPY_PROMPT \ + "Extract needs cabinet file '%1'. Please insert the disk\n" \ + "labeled '%2' into drive %3, and press ENTER. You may\n" \ + "enter a different drive and/or path:" + + + /*LOCALIZE + * + * Purpose: Prompt for next cabinet on a hard disk (non-removable media) + * %1 = Name of desired cabinet file + * %2 = Disk label with desired cabinet + * Generate: Copy loctest1.cab to directory FOO, "cd FOO", *don't* put + * loctest2.cab in the FOO directory, then type + * "EXTRACT loctest1.cab m.asc" + * Expect: + * "Extract needs cabinet file 'loctest2.cab' from 'Disk 2'." + * "Please enter the path where this file may be" + * "found, and press ENTER:" + */ +#define pszEXT_NOFLOPPY_PROMPT \ + "Extract needs cabinet file '%1' from '%2'.\n" \ + "Please enter the path where this file may be\n" \ + "found, and press ENTER:" + +/* + ** Error messages + */ + + /*LOCALIZE + * + * Purpose: Prefix string for *all* error messages. + * %1 = Detailed error message (one of pszEXTERR_... below) + * Generate: Type "EXTRACT /x" (bad switch) + * Expect: "ERROR: Invalid switch: /x" + * ^^^^^^^------------------ + */ +#define pszEXTERR_ERROR "ERROR: %1" + + + /*LOCALIZE + * + * Purpose: Indicate an unknown or bad command line switch. + * %1 = Bad switch string + * Generate: Type "EXTRACT /x" (bad switch) + * Expect: "ERROR: Invalid switch: /x" + * ^^^^^^^^^^^^^^^^-- + */ +#define pszEXTERR_BAD_SWITCH "Invalid switch: %1" + + + /*LOCALIZE + * + * Purpose: Indicate a switch that is inconsistent with a previous switch. + * %1 = Bad switch string + * Generate: Type "EXTRACT /d /e" (Can't do directory & extract both) + * Expect: "ERROR: Conflicting switch: e" + * ^^^^^^^^^^^^^^^^^^^^- + */ +#define pszEXTERR_CONFLICTING_SWITCH "Conflicting switch: %1" + + + /*LOCALIZE + * + * Purpose: OUT OF MEMORY trying to initialize EXTRACT. + * Generate: Difficult -- have to get very low memory situation, + * then type "EXTRACT". + * Expect: "ERROR: Could not allocate SESSION" + */ +#define pszEXTERR_NO_SESSION "Could not allocate SESSION" + + + /*LOCALIZE + * + * Purpose: Indicate command line syntax error -- /L with no path. + * Generate: "EXTRACT /L" (location missing) + * Expect: "ERROR: Location missing" + */ +#define pszEXTERR_MISSING_LOCATION "Location missing" + + + /*LOCALIZE + * + * Purpose: Indicate general command line syntax error. + * Generate: "EXTRACT /C" (no files specified) + * Expect: "ERROR: Bad parameters" + */ +#define pszEXTERR_BAD_PARAMETERS "Bad parameters" + + + /*LOCALIZE + * + * Purpose: Indicate command line syntax error -- /L used twice. + * Generate: "EXTRACT /L foo /L bar" (two locations) + * Expect: "ERROR: Location specified more than once" + */ +#define pszEXTERR_LOCATION_TWICE "Location specified more than once" + + + /*LOCALIZE + * + * Purpose: OUT OF MEMORY saving a command-line filespec. + * %1 = filespec being added. + * Generate: Difficult -- have to get very low memory situation, then + * type "EXTRACT loctest1.cab *.exe *.dll *.txt". + * Expect: "ERROR: Could not add filespec: *.txt" + */ +#define pszEXTERR_COULD_NOT_ADD_FILE "Could not add filespec: %1" + + + /*LOCALIZE + * + * Purpose: Command line syntax error -- no cabinet file name specified. + * Generate: Type "EXTRACT /A". + * Expect: "ERROR: Must supply a cabinet file name" + */ +#define pszEXTERR_MISSING_CABINET "Must supply a cabinet file name" + + + /*LOCALIZE + * + * Purpose: INTERNAL ERROR -- FDIDestroy() call failed. + * Generate: This is probably impossible to generate -- don't try. + * Expect: "ERROR: FDIDestroy failed" + */ +#define pszEXTERR_FDIDESTROY_FAILED "FDIDestroy failed" + + + /*LOCALIZE + * + * Purpose: Could not create file being extracted. + * %1 = name of file Extract was trying to create + * Generate: Put a write-protected disk in drive A:, type + * "EXTRACT /L a:\ loctest1.cab a.asc". + * NOTE: Testing under NT I couldn't get this message -- + * MS-DOS/Win16 may be more fruitful. + * Expect: "ERROR: Cannot create file: a.asc" + */ +#define pszEXTERR_CANNOT_CREATE_FILE "Cannot create file: %1" + + + /*LOCALIZE + * + * Purpose: Could not open cabinet file. + * %1 = name of cabinet file that cannot be opened + * Generate: Type "EXTRACT /D junk.jnk" (junk.jnk does not exist) + * Expect: "ERROR: Cannot open file: junk.jnk" + */ +#define pszEXTERR_CANNOT_OPEN_FILE "Cannot open file: %1" + + + /*LOCALIZE + * + * Purpose: Specified file is not a cabinet file. + * %1 = name of cabinet file that is not a cabinet file + * Generate: Type "EXTRACT /D extract.exe" + * Expect: "ERROR: extract.exe is not a cabinet file" + */ +#define pszEXTERR_NOT_A_CABINET "%1 is not a cabinet file" + + + /*LOCALIZE + * + * Purpose: Indicate user cancelled EXTRACT.EXE. + * Generate: Type "MD junk", "COPY loctest1.cab JUNK", "CD junk", + * "EXTRACT /AD loctest1.cab", and when you are prompted + * for the next cabinet, type CTRL+Z and press ENTER. + * Expect: "ERROR: Cancelled" + */ +#define pszEXTERR_ABORT "Cancelled" + + +/* + ** FDI error messages + */ + + /*LOCALIZE + * + * Purpose: Could not create a temporary file that the decompressor + * needs to store data that overflows its RAM workspace. + * %1 = name of cabinet file being processed + * %2 = name of temporary file that could not be created + * Generate: Drive D:\ is read-only -- a CD-ROM drive or a read-only + * network connection, for example. + * Type: + * SET TMP=D:\ + * EXTRACT /E LOCTEST3.CAB + * Expect: "ERROR: Could not create temporary file while decompressing Q121.CAB: esf2" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------^^---- + */ +#define pszFDIERR_SPILL_CREATE "Could not create temporary file for %1: %2" + + + /*LOCALIZE + * + * Purpose: Not enough space for temporary file that the decompressor + * needs to store data that overflows its RAM workspace. + * %1 = name of cabinet file being processed + * %2 = name of temporary file + * %3 = required size of temporary file + * Generate: Drive D:\ has less than 2Mb of free space, but is writeable. + * Type: + * SET TMP=D:\ + * EXTRACT /E LOCTEST3.cab + * Expect: "ERROR: Not enough space for temporary file esf2 while decompressing Q121.CAB: 20971523 bytes needed"" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_SPILL_SIZE "Not enough space for temporary file %2 while decompressing %1: %3 bytes needed" + + + /*LOCALIZE + * + * Purpose: FDI could not find the specified cabinet file. + * %1 = name of cabinet file that cannot be opened + * Generate: Difficult -- Extract made sure the cabinet file was present + * before calling FDI! DO NOT TRY TO REPRO. + * Expect: "ERROR: Could not find cabinet file loctest1.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_CAB_NOT_FOUND "Could not find cabinet file %1" + + + /*LOCALIZE + * + * Purpose: FDI determined cabinet file header is incorrect. + * %1 = name of cabinet file that is not a cabinet file + * Generate: Difficult -- Extract called FDI first to see if the + * before calling FDI! DO NOT TRY TO REPRO. + * Expect: "ERROR: loctest1.cab is not a cabinet file" + * ------------^^^^^^^^^^^^^^^^^^^^^^ + */ +#define pszFDIERR_NOT_A_CABINET "%1 is not a cabinet file" + + + /*LOCALIZE + * + * Purpose: FDI does not recognize the cabinet version number. + * %1 = name of cabinet file + * %2 = Version number found in cabinet file + * Generate: Difficult - Use a binary file editor (DEBUG) to change the + * word at offset 26 decimal (1A hex) in loctest1.cab to + * 0xFFFF, and save it as bad.cab, then type + * "EXTRACT /D bad.cab" + * Expect: "ERROR: Cabinet file bad.cab has an unknown version(FFFF)" + * ^^^^^^^^^^^^^-------^^^^^^^^^^^^^^^^^^^^^^^^----^ + */ +#define pszFDIERR_BAD_CAB_VER "Cabinet file %1 has an unknown version(%2)" + + + /*LOCALIZE + * + * Purpose: FDI detected a corruption in the cabinet format. + * %1 = name of cabinet file + * Generate: Difficult - Use a binary file editor (DEBUG) to write out + * the first 120 bytes of loctest1.cab to bad.cab, then type + * "EXTRACT /D bad.cab" + * Expect: "ERROR: Cabinet file bad.cab is corrupted" + * ^^^^^^^^^^^^^-------^^^^^^^^^^^^^ + */ +#define pszFDIERR_CORRUPT_CAB "Cabinet file %1 is corrupted" + + + /*LOCALIZE + * + * Purpose: OUT OF MEMORY initializing FDI. + * Generate: Difficult -- have to get very low memory situation, + * then type "EXTRACT /AE loctest1.cab" + * Expect: "ERROR: Out of memory while processing cabinet file loctest1.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_ALLOC_FAIL "Out of memory while processing cabinet file %1" + + + /*LOCALIZE + * + * Purpose: FDI detected an unsupported compression type in the cabinet. + * Generate: Difficult -- have to find a CFFOLDER structure and then trash + * the typeCompression field -- DO NOT TRY TO REPRO. + * Expect: "ERROR: Unknown compression type in cabinet file loctest1.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_BAD_COMPR_TYPE "Unknown compression type in cabinet file %1" + + + /*LOCALIZE + * + * Purpose: FDI detected corrupted compressed data in the cabinet. + * Generate: Difficult -- have to find a CFDATA structure and then trash + * the compressed data area -- DO NOT TRY TO REPRO. + * Expect: "ERROR: Failure decompressing cabinet file loctest1.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_MDI_FAIL "Failure decompressing cabinet file %1" + + + /*LOCALIZE + * + * Purpose: FDI had problems writing out an extracted file. + * Generate: Difficult -- perhaps try telling extract to put files out to + * to a disk that has only 8K of space, i.e., type + * "EXTRACT /AEL X: loctest1.cab" where X: has 8K free space. + * Expect: "ERROR: Error creating destination file a.asc from cabinet file loctest1.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_TARGET_FILE "Error creating destination file %1 from cabinet file %2" + + + /*LOCALIZE + * + * Purpose: FDI detected inconsistent RESERVE settings in a cabinet set. + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "ERROR: RESERVE size mismatch in cabinet file loctest2.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_RESERVE_MISMATCH "RESERVE size mismatch in cabinet file %1" + + + /*LOCALIZE + * + * Purpose: FDI did not get back the expected continuation cabinet. + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "ERROR: Wrong continuation cabinet file loctest2.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_WRONG_CABINET "Wrong continuation cabinet file %1" + + + /*LOCALIZE + * + * Purpose: FDI was aborted. + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "ERROR: User aborted while processing cabinet file loctest1.cab" + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_USER_ABORT "User aborted while processing cabinet file %1" + + /*LOCALIZE + * + * Purpose: INTERNAL ERROR - FDI returned unknown error code. + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "ERROR: Unknown error(??) processing cabinet file loctest2.cab" + * ^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^------------ + */ +#define pszFDIERR_UNKNOWN_ERROR "Unknown error(%1) processing cabinet file %2" + + +//*** THE END - extract.msg diff --git a/private/windows/diamond/filelist.c b/private/windows/diamond/filelist.c new file mode 100644 index 000000000..6eed7adb6 --- /dev/null +++ b/private/windows/diamond/filelist.c @@ -0,0 +1,358 @@ +/*** filelist.c - File List Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 20-Aug-1993 bens Initial version + * 21-Aug-1993 bens Add more set/query operations + * 10-Feb-1994 bens Add comments to FLDestroyList + * 15-Feb-1994 bens Fix bug in FLSetDestination + * 01-Apr-1994 bens Added FLSetSource() message + * + * Exported Functions: + * FLAddFile - Add file spec to a file list + * FLCreateList - Create a file list + * FLDestroyList - Destroy a file list + * FLFirstFile - Get first file spec from a file list + * FLGetDestination - Get destination file name + * FLGetGroup - Get group/disk number for a file spec + * FLGetSource - Get source file name + * FLNextFile - Get next file spec + * FLPreviousFile - Get previous file spec + * FLSetSource - Change source file name + * FLSetDestination - Change destination file name + * FLSetGroup - Set group/disk number for a file spec + */ + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" + +#include "filelist.h" + +#include "filelist.msg" // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath" + + +typedef struct FILESPEC_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFILESPEC +#endif + char *pszSrc; // Source file name + char *pszDst; // Destination file name + GROUP grp; // Group status / Disk Number + struct FILESPEC_t *pfspecPrev; // Previous filespec in list + struct FILESPEC_t *pfspecNext; // Next filespec in list +} FILESPEC; /* fspec */ +typedef FILESPEC *PFILESPEC; /* pfspec */ +#ifdef ASSERT +#define sigFILESPEC MAKESIG('F','S','P','C') // FILESPEC signature +#define AssertFSpec(pv) AssertStructure(pv,sigFILESPEC); +#else // !ASSERT +#define AssertFSpec(pv) +#endif // !ASSERT + + +typedef struct FILELIST_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFILELIST +#endif + PFILESPEC pfspecHead; + PFILESPEC pfspecTail; +} FILELIST; /* flist */ +typedef FILELIST *PFILELIST; /* pflist */ +#ifdef ASSERT +#define sigFILELIST MAKESIG('F','L','S','T') // FILELIST signature +#define AssertFList(pv) AssertStructure(pv,sigFILELIST); +#else // !ASSERT +#define AssertFList(pv) +#endif // !ASSERT + +#define HFSfromPFS(hfs) ((PFILESPEC)(hfs)) +#define PFSfromHFS(pfs) ((HFILESPEC)(pfs)) + +#define HFLfromPFL(hfl) ((PFILELIST)(hfl)) +#define PFLfromHFL(pfl) ((HFILELIST)(pfl)) + + + +/*** FLAddFile - Add file spec to a file list + * + * NOTE: See filelist.h for entry/exit conditions. + */ +HFILESPEC FLAddFile(HFILELIST hflist,char *pszSrc,char *pszDst,PERROR perr) +{ + PFILESPEC pfspec; + PFILELIST pflist; + + pflist = PFLfromHFL(hflist); + AssertFList(pflist); + Assert(pszSrc != NULL); + + //** Create file specification + if (!(pfspec = MemAlloc(sizeof(FILESPEC)))) { + goto error; + } + + //** Intialize structure enough so that clean-up routine can determine + // if any resources need to be freed. + pfspec->pszSrc = NULL; + pfspec->pszDst = NULL; + SetAssertSignature(pfspec,sigFILESPEC); + + //** Make copy of source name + if (!(pfspec->pszSrc = MemStrDup(pszSrc))) { + goto error; + } + + //** pszDst is optional, may be NULL! + if (pszDst == NULL) { + pfspec->pszDst = NULL; + } + else if (!(pfspec->pszDst = MemStrDup(pszDst))) { + goto error; + } + + //** Finishing initializing file spec, and link onto list + pfspec->grp = grpNONE; // Assume no group + pfspec->pfspecNext = NULL; // Always last on list + pfspec->pfspecPrev = pflist->pfspecTail; // Always points to last file spec + + if (pflist->pfspecHead == NULL) { // File list is empty + pflist->pfspecHead = pfspec; + pflist->pfspecTail = pfspec; + } + else { // File list is not empty + AssertFSpec(pflist->pfspecTail); + pflist->pfspecTail->pfspecNext = pfspec; // Add to end of list + pflist->pfspecTail = pfspec; // New tail + } + + // Success + return HFSfromPFS(pfspec); + +error: + if (!pfspec) { + if (!(pfspec->pszSrc)) { + MemFree(pfspec->pszSrc); + } + if (!(pfspec->pszDst)) { + MemFree(pfspec->pszDst); + } + MemFree(pfspec); + } + + ErrSet(perr,pszFLISTERR_OUT_OF_MEMORY,"%s",pszADDING_FILE); + return NULL; // Failure + +} /* FLAddFile */ + + +/*** FLCreateList - Create a file list + * + * NOTE: See filelist.h for entry/exit conditions. + */ +HFILELIST FLCreateList(PERROR perr) +{ + PFILELIST pflist; + + if (!(pflist = MemAlloc(sizeof(FILELIST)))) { + ErrSet(perr,pszFLISTERR_OUT_OF_MEMORY,"%s",pszCREATING_FILE_LIST); + return FALSE; + } + + pflist->pfspecHead = NULL; + pflist->pfspecTail = NULL; + SetAssertSignature(pflist,sigFILELIST); + + return HFLfromPFL(pflist); + +} /* FLCreateList */ + + +/*** FLDestroyList - Destroy a file list + * + * NOTE: See filelist.h for entry/exit conditions. + */ +BOOL FLDestroyList(HFILELIST hflist,PERROR perr) +{ +//BUGBUG 20-Aug-1993 bens FLDestroy not implemented + AssertForce("FLDestroy() not implemented!",__FILE__,__LINE__); +// ClearAssertSignature(pflist); +// ClearAssertSignature(pfspec); + return TRUE; +} + + +/*** FLFirstFile - Get first file spec from a file list + * + * NOTE: See filelist.h for entry/exit conditions. + */ +HFILESPEC FLFirstFile(HFILELIST hflist) +{ + PFILELIST pflist; + + pflist = PFLfromHFL(hflist); + AssertFList(pflist); + + return HFSfromPFS(pflist->pfspecHead); +} + + +/*** FLNextFile - Get next file spec + * + * NOTE: See filelist.h for entry/exit conditions. + */ +HFILESPEC FLNextFile(HFILESPEC hfspec) +{ + PFILESPEC pfspec; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + return HFSfromPFS(pfspec->pfspecNext); +} + + +/*** FLPreviousFile - Get previous file spec + * + * NOTE: See filelist.h for entry/exit conditions. + */ +HFILESPEC FLPreviousFile(HFILESPEC hfspec) +{ + PFILESPEC pfspec; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + return HFSfromPFS(pfspec->pfspecPrev); +} + + +/*** FLGetGroup - Get group/disk number for a file spec + * + * NOTE: See filelist.h for entry/exit conditions. + */ +GROUP FLGetGroup(HFILESPEC hfspec) +{ + PFILESPEC pfspec; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + return pfspec->grp; +} + + +/*** FLGetDestination - Get destination file name + * + * NOTE: See filelist.h for entry/exit conditions. + */ +char *FLGetDestination(HFILESPEC hfspec) +{ + PFILESPEC pfspec; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + return pfspec->pszDst; + +} /* FLGetDestination */ + + +/*** FLGetSource - Get source file name + * + * NOTE: See filelist.h for entry/exit conditions. + */ +char *FLGetSource(HFILESPEC hfspec) +{ + PFILESPEC pfspec; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + return pfspec->pszSrc; + +} /* FLGetSource */ + + +/*** FLSetGroup - Set group/disk number for a file spec + * + * NOTE: See filelist.h for entry/exit conditions. + */ +void FLSetGroup(HFILESPEC hfspec,GROUP grp) +{ + PFILESPEC pfspec; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + pfspec->grp = grp; +} + + +/*** FLSetSource - Change source file name + * + * NOTE: See filelist.h for entry/exit conditions. + */ +BOOL FLSetSource(HFILESPEC hfspec, char *pszSrc, PERROR perr) +{ + PFILESPEC pfspec; + char *pszOriginal; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + //** Save original destination, so we can free it later + pszOriginal = pfspec->pszSrc; + + //** Set new destination + if (!(pfspec->pszSrc = MemStrDup(pszSrc))) { + ErrSet(perr,pszFLISTERR_OUT_OF_MEMORY,"%s",pszCHANGING_SOURCE); + return FALSE; // Failure + } + + //** Free old destination + if (pszOriginal) { + MemFree(pszOriginal); + } + + //** Success + return TRUE; +} + + +/*** FLSetDestination - Change destination file name + * + * NOTE: See filelist.h for entry/exit conditions. + */ +BOOL FLSetDestination(HFILESPEC hfspec, char *pszDst, PERROR perr) +{ + PFILESPEC pfspec; + char *pszDstOriginal; + + pfspec = PFSfromHFS(hfspec); + AssertFSpec(pfspec); + + //** Save original destination, so we can free it later + pszDstOriginal = pfspec->pszDst; + + //** Set new destination + if (!(pfspec->pszDst = MemStrDup(pszDst))) { + ErrSet(perr,pszFLISTERR_OUT_OF_MEMORY,"%s",pszCHANGING_DESTINATION); + return FALSE; // Failure + } + + //** Free old destination + if (pszDstOriginal) { + MemFree(pszDstOriginal); + } + + //** Success + return TRUE; +} diff --git a/private/windows/diamond/filelist.h b/private/windows/diamond/filelist.h new file mode 100644 index 000000000..d19fb1b71 --- /dev/null +++ b/private/windows/diamond/filelist.h @@ -0,0 +1,219 @@ +/*** filelist.h - Definitions for File List Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 20-Aug-1993 bens Initial version + * 21-Aug-1993 bens Add more set/query operations + * 01-Apr-1994 bens Added FLSetSource() message + * + * Exported Functions: + * FLCreateList - Create a file list + * FLDestroyList - Destroy a file list + * + * FLAddFile - Add file spec to a file list + * + * FLFirstFile - Get first file spec from a file list + * FLNextFile - Get next file spec + * FLPreviousFile - Get previous file spec + * + * FLGetDestination - Get destination file name + * FLGetGroup - Get group/disk number for a file spec + * FLGetSource - Get source file name + * + * FLSetSource - Change source file name + * FLSetDestination - Change destination file name + * FLSetGroup - Set group/disk number for a file spec + */ + +#include "error.h" + +//** PUBLIC definitions + +typedef int GROUP; /* grp */ +#define grpBAD 0 // Bad group value +#define grpNONE -1 // File is not in a group +#define grpSTART -2 // File is first file in a group +#define grpMIDDLE -3 // File is in a group +#define grpEND -4 // File is last file in a group + +typedef void *HFILESPEC; /* hfspec */ +typedef void *HFILELIST; /* hflist */ + + +/*** FLAddFile - Add file spec to a file list + * + * Entry: + * hflist - List to add to + * pszSrc - Source file name + * pszDst - Destination file name (NULL if not specified) + * perr - ERROR structure + * + * Exit-Success: + * Returns HFILESPEC of newly added file spec + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HFILESPEC FLAddFile(HFILELIST hflist,char *pszSrc,char *pszDst,PERROR perr); + + +/*** FLCreateList - Create a file list + * + * Entry: + * perr - ERROR structure + * + * Exit-Success: + * Returns HFILELIST of newly created file list + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HFILELIST FLCreateList(PERROR perr); + + +/*** FLDestroyList - Destroy a file list + * + * Entry: + * hflist - List to destroy + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; file list destroyed + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +BOOL FLDestroyList(HFILELIST hflist,PERROR perr); + + +/*** FLFirstFile - Get first file spec from a file list + * + * Entry: + * hflist - List to Get + * + * Exit-Success: + * Returns HFILESPEC of first file spec in file list. + * + * Exit-Failure: + * Returns NULL; hflist is bad or empty. + */ +HFILESPEC FLFirstFile(HFILELIST hflist); + + +/*** FLNextFile - Get next file spec + * + * Entry: + * hfspec - File spec + * + * Exit-Success: + * Returns HFILESPEC of next file spec following hfspec. + * + * Exit-Failure: + * Returns NULL; no more file specs, or hfspec is bad. + */ +HFILESPEC FLNextFile(HFILESPEC hfspec); + + +/*** FLPreviousFile - Get previous file spec + * + * Entry: + * hfspec - File spec + * + * Exit-Success: + * Returns HFILESPEC of file spec immediately preceding hfspec. + * + * Exit-Failure: + * Returns NULL; no more file specs, or hfspec is bad. + */ +HFILESPEC FLPreviousFile(HFILESPEC hfspec); + + +/*** FLGetGroup - Get group/disk number for a file spec + * + * Entry: + * hfspec - File spec to get + * + * Exit-Success: + * Returns GROUP (or disk number) of file spec. + * + * Exit-Failure: + * Returns grpBAD; hfspec was bad + */ +GROUP FLGetGroup(HFILESPEC hfspec); + + +/*** FLGetDestination - Get destination file name + * + * Entry: + * hfspec - File spec to get + * + * Exit-Success: + * Returns destination file name + * + * Exit-Failure: + * Returns NULL; no destination file name specified + */ +char *FLGetDestination(HFILESPEC hfspec); + + +/*** FLGetSource - Get source file name + * + * Entry: + * hfspec - File spec to get + * + * Exit-Success: + * Returns source file name + * + * Exit-Failure: + * Returns NULL; no source file name specified + */ +char *FLGetSource(HFILESPEC hfspec); + + +/*** FLSetGroup - Set group/disk number for a file spec + * + * Entry: + * hfspec - File spec + * + * Exit-Success: + * Group/Disk number updated + */ +void FLSetGroup(HFILESPEC hfspec,GROUP grp); + + +/*** FLSetSource - Change source file name + * + * Entry: + * hfspec - File spec to change + * pszSrc - New source file name + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; destination updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL FLSetSource(HFILESPEC hfspec, char *pszSrc, PERROR perr); + + +/*** FLSetDestination - Change destination file name + * + * Entry: + * hfspec - File spec to change + * pszDst - New destination file name + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; destination updated. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL FLSetDestination(HFILESPEC hfspec, char *pszDst, PERROR perr); diff --git a/private/windows/diamond/filelist.msg b/private/windows/diamond/filelist.msg new file mode 100644 index 000000000..a519f7fbe --- /dev/null +++ b/private/windows/diamond/filelist.msg @@ -0,0 +1,65 @@ +/*** filelist.msg - Displayable strings for filelist.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 20-Aug-1993 bens Initial version + * 01-Apr-1994 bens Added FLSetSource() message + * 07-Jun-1994 bens Add localization comments (whew!) + */ + +/* + ** Error Messages + */ + + /*LOCALIZE + * + * Purpose: Out of memory doing file list management. + * %1 = Detailed error message (one of psz_... below) + * Generate: Difficult -- have to create low memory situation. + * DO NOT TRY TO REPRO. + */ +#define pszFLISTERR_OUT_OF_MEMORY "Out of memory: %1" + + + /*LOCALIZE + * + * Purpose: Out of memory creating a file list. + * Generate: Difficult -- have to create low memory situation. + * DO NOT TRY TO REPRO. + */ +#define pszCREATING_FILE_LIST "Creating file list" // For out of mem + + + /*LOCALIZE + * + * Purpose: Out of memory adding a file to a file list. + * Generate: Difficult -- have to create low memory situation. + * DO NOT TRY TO REPRO. + */ +#define pszADDING_FILE "Adding a file" // For out of mem + + + /*LOCALIZE + * + * Purpose: Out of memory changing the destination name in a file list. + * Generate: Difficult -- have to create low memory situation. + * DO NOT TRY TO REPRO. + */ +#define pszCHANGING_DESTINATION "Changing destination" // For out of mem + + + /*LOCALIZE + * + * Purpose: Out of memory changing the source name in a file list. + * Generate: Difficult -- have to create low memory situation. + * DO NOT TRY TO REPRO. + */ +#define pszCHANGING_SOURCE "Changing source" // For out of mem + +//*** THE END - filelist.msg diff --git a/private/windows/diamond/filetype.c b/private/windows/diamond/filetype.c new file mode 100644 index 000000000..80cc1d408 --- /dev/null +++ b/private/windows/diamond/filetype.c @@ -0,0 +1,615 @@ +/*** FILETYPE.C - Determine file type + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * History: + * 10-Feb-1994 bens Initial version + */ + +#include <ctype.h> +#include <fcntl.h> +#include <io.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +//** Get minimal Win32 definitions +#define WIN32_LEAN_AND_MEAN +#include <windows.h> //** Includes ntimage.h definitions! + +#include "types.h" + + +//***************************************************************************** +//* CONSTANTS * +//***************************************************************************** + +#define szProduct "FILETYPE" // product name + +#define verMAJOR 0 // Major version number (N.xxx) +#define verMINOR 10 // Minor version number (x.NN) + +#define cMAX_FILES 40 // Maximum number of cmd line file specs + +#define cbOUTPUTLINEMAX 100 // Maximum line length on stdout + +#define chSWITCH '/' // Command line switch character +#define chSWITCH2 '-' // Alternate Command line switch character + +#define cchMAXFILEPATH 256 // Maximum length of a file path + +#define BITMAP_SIGNATURE 0x4D42 // BM - *.bmp +#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ +#define IMAGE_WIN_SIGNATURE 0x454E // NE +#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE + +//***************************************************************************** +//* TYPES * +//***************************************************************************** + +typedef struct { + char *pb; // Memory pointer + ULONG cb; // Size of file + HANDLE hfm; // File Mapping handle + HANDLE hf; // File handle + char ach[cchMAXFILEPATH+1]; // File name +} MEMORYMAPPEDFILE; /* mmf */ +typedef MEMORYMAPPEDFILE *PMEMORYMAPPEDFILE; /* pmmf */ + + +typedef enum { + ftUNKNOWN, // Unknown file type + + ftBITMAP, // *.BMP + ftDOSEXE, // 16-bit EXE + ftVXD, // VxD (*.386) + ftWIN16, // Win16 EXE/DLL + ftWIN32, // Win32 EXE/DLL +} FILETYPE; /* ft */ + +/*** GLOBAL - structure for global variables + * + * This is to make them obvious in the sources + * + */ +typedef struct { + int argc; // argc parameter passed to main(...) + char **argv; // argv parameter passed to main(...) + char ach[cbOUTPUTLINEMAX]; // Line output buffer + int cFiles; // Count of file specs on cmd line + char *apszFile[cMAX_FILES]; // File specs on cmd line +} GLOBAL; + + +//***************************************************************************** +//* MACROS * +//***************************************************************************** + + +/*** dwordMMF - extract DWORD from a memory mapped file + * + * Entry: + * pb - pointer to memory mapped file + * ul - offset from pointer + * + * Exit: + * returns dword at pv+ul; + */ +#define dwordMMF(pb,ul) (*((DWORD *)((char *)pb + (ul)))) + + +//***************************************************************************** +//* VARIABLES * +//***************************************************************************** + + +/*** g - Global variables + * + */ +GLOBAL g; + + +/*** apszSyntax - Syntax help + * + */ + +#define isynSUMMARY 2 // Index of syntax summary line + +char *apszSyntax[] = { + "Determine type of file." + "", + "FILETYPE file [file2 ...]", // Must be isynSUMMARY'nd line + "", + " file File to examine; wild cards are supported", +}; + + +//***************************************************************************** +//* FUNCTION PROTOTYPES * +//***************************************************************************** + + +void doFileSpec(char *pszFileName); +FILETYPE ftFromFile(PMEMORYMAPPEDFILE pmmf); + +int UnmapFile(PMEMORYMAPPEDFILE pmmf); +BOOL MapFile(PMEMORYMAPPEDFILE pmmf, char *psz, BOOL fWrite); +char * StringFromWin32ErrorCode(DWORD error); + + +void Error(char *pszMsg, char *pszParm); +int __cdecl main(int argc, char **argv); +void ParseArgs(int argc, char **argv); +void PrintBanner(void); +void ShowSyntax(BOOL fFull); + + +//***************************************************************************** +//* FUNCTIONS * +//***************************************************************************** + + +/*** main - entry point + * + */ +int __cdecl main(int argc, char **argv) +{ + int i; + long hfind; + struct _finddata_t fileinfo; + + // Get arguments + ParseArgs(argc,argv); // If error, it exits + + // Process file names + for (i=0; i<g.cFiles; i++) { + hfind = _findfirst(g.apszFile[i],&fileinfo); + if (hfind != -1) { + do { + if (!(fileinfo.attrib & _A_SUBDIR)) { + doFileSpec(fileinfo.name); + } + } + while (_findnext(hfind,&fileinfo) == 0); + _findclose(hfind); + } + } + + // Done + return 0; +} + +void doFileSpec(char *pszFileName) +{ + FILETYPE ft; + MEMORYMAPPEDFILE mmf; + char *psz; + char *pszType; + char *pszExtension; + + // Map EXE file into memory + if (!MapFile(&mmf,pszFileName,FALSE)) { + Error("Could not memory map %s",pszFileName); + } + + // Get file type + ft = ftFromFile(&mmf); + switch (ft) { + case ftBITMAP: pszType = "bitmap"; break; + case ftDOSEXE: pszType = "dosexe"; break; + case ftVXD: pszType = "vxd"; break; + case ftWIN16: pszType = "win16"; break; + case ftWIN32: pszType = "win32"; break; + + case ftUNKNOWN: + default: + pszType = "unknown"; break; + } + + // Find extension (if present) + pszExtension = NULL; // No extension + psz = mmf.ach; // Start at front of file name + while (psz) { + psz = strpbrk(psz,"/\\."); // Find a period or a path separator + if (psz) { // Found something + if (*psz == '.') { // Found a period + pszExtension = psz+1; // Point after period -- possible extension + } + else { + pszExtension = NULL; // Any extension we thought we had was not + // part of the file name! + } + psz++; // Skip over special character + } + } + if (pszExtension == NULL) { // Make sure we point to something! + pszExtension = ""; + } + + // Display info + printf("%s,%s,%d,%s\n",pszType,pszExtension,mmf.cb,mmf.ach); + + // Unmap and close EXE file + if (!UnmapFile(&mmf)) { + Error("Could not unmap file %s",pszFileName); + } +} + + +/*** ftFromFile - Determine file type + * + * Entry: + * pmmf - Mapping to file + * + * Exit: + * Returns FILETYPE. + */ +FILETYPE ftFromFile(PMEMORYMAPPEDFILE pmmf) +{ + PIMAGE_DOS_HEADER pidh; // MS-DOS header + PIMAGE_NT_HEADERS pinh; // Complete PE header + USHORT *pus; // Pointer to NE header (maybe) + + //** Make sure file is large enough for 'MZ' signature + if (pmmf->cb < sizeof(pidh->e_magic)) { + return ftUNKNOWN; // Not an EXE + } + + //** Check MZ signature + pidh = (PIMAGE_DOS_HEADER)pmmf->pb; // Pointer to MS-DOS header + if (pidh->e_magic != IMAGE_DOS_SIGNATURE) { + if (pidh->e_magic == BITMAP_SIGNATURE) { + return ftBITMAP; + } + else { + return ftUNKNOWN; // Nothing we know about + } + } + + //** We know it is at least an MS-DOS EXE + + //** Make sure file is large enough for 'NE'/'LE'/'PE' signature + if (pmmf->cb < (pidh->e_lfanew + sizeof(pinh->Signature))) { + return ftDOSEXE; // Not fancier EXE + } + + //** Check for NE and LE signatures + pus = (USHORT *)(pmmf->pb + pidh->e_lfanew); + if (*pus == IMAGE_WIN_SIGNATURE) { + return ftWIN16; + } + if (*pus == IMAGE_OS2_SIGNATURE_LE) { + return ftVXD; + } + + //** Check PE signature + pinh = (PIMAGE_NT_HEADERS)pus; // PE header? + if (pinh->Signature == IMAGE_NT_SIGNATURE) { + return ftWIN32; + } + + //** Assume it is a DOS EXE + return ftDOSEXE; +} + + +/*** Error - format error message, display it, and exit + * + * Entry: + * pszMsg - error message + * pszParm - replacable parm for %s in pszMsg + * + * Exit: + * message formatted and displayed + * exit program + */ +void Error(char *pszMsg, char *pszParm) +{ + printf("Error: "); + printf(pszMsg,pszParm); + printf("\n"); + exit(1); +} + + +/*** ParseArgs - Parse command-line arguments + * + * Entry + * argc - count of arguments + * argv - array of arguments + * + * Exit-Success + * GLOBAL structure (g) fields filled in + * + * Exit-Failure + * Prints message and exits program. + */ +void ParseArgs(int argc, char **argv) +{ + char ch; + int i; + char *pch; + + // Save command line pointers for later + g.argc = argc; + g.argv = argv; + g.cFiles = 0; // No files seen, yet + + // Parse each argument + for (i=1; i<argc; i++) { + if ( (argv[i][0] == chSWITCH) || + (argv[i][0] == chSWITCH2) ) { + pch = &argv[i][1]; // Start with first character + while (*pch) { + ch = (char)toupper((int)*pch); + switch (ch) { + + case '?': + ShowSyntax(1); + break; + + default: + g.ach[0] = *pch; + g.ach[1] = '\0'; + Error("Unknown switch: %s",g.ach); + } + pch++; + } + } + else { // Must be a file spec + g.apszFile[g.cFiles++] = argv[i]; + } + } + + // ** If no file name, give error + if (g.cFiles == 0) + Error("Must specify a file name.",NULL); +} + + +/*** ShowSyntax - Display command-line syntax + * + */ +void ShowSyntax(BOOL fFull) +{ + int i; + int cLines; + + // Announce ourselves + PrintBanner(); + + if (fFull) { // Show full help + cLines = sizeof(apszSyntax)/sizeof(char *); + for (i=0; i<cLines; i++) { + printf("%s\n",apszSyntax[i]); + } + } + else { // Just show summary line + printf("%s\n",apszSyntax[isynSUMMARY]); + } + + exit(0); +} + + +/*** PrintBanner - print banner for this program + * + */ +void PrintBanner(void) +{ + printf("%s - Version %d.%02d\n",szProduct,verMAJOR,verMINOR); + printf("Copyright 1992,1993 Microsoft Corp.\n"); +} + + +/*** MapFile - Map existing file to memory address + * + * Entry: + * pmmf - Pointer to structure to receive mapping information + * psz - File name + * fWrite - TRUE => read/write access, else read-only access + * + * Exit-Success: + * Returns TRUE; pmmf filled in + * + * Exit-Failure: + * Returns FALSE. + */ +BOOL MapFile(PMEMORYMAPPEDFILE pmmf, char *psz, BOOL fWrite) +{ + ULONG ul; + DWORD fdwAccess; + DWORD fdwShareMode; + DWORD fdwProtect; + DWORD fdwAccessMapping; + + // Construct access settings + if (fWrite) { + fdwAccess = GENERIC_READ | GENERIC_WRITE; + fdwShareMode = 0; // Do not permit any other access + fdwProtect = PAGE_READWRITE; + fdwAccessMapping = FILE_MAP_WRITE; + } + else { + fdwAccess = GENERIC_READ; + fdwShareMode = FILE_SHARE_READ; // Allow other readers + fdwProtect = PAGE_READONLY; + fdwAccessMapping = FILE_MAP_READ; + } + + //** Clear structure, to simplify error path + pmmf->pb = NULL; + pmmf->cb = 0; + pmmf->hfm = NULL; + pmmf->hf = NULL; + pmmf->ach[0] = '\0'; + + //** Open file + pmmf->hf = CreateFile(psz, // file name + fdwAccess, // r/w or read-only + fdwShareMode, // allow nothing or allow reading + NULL, // default security + OPEN_EXISTING,// file must exist + 0, // file attributes are don't care + NULL); // no template file + + if (!pmmf->hf) { + ul = GetLastError(); // Get last error + Error("Cannot open file: %s", StringFromWin32ErrorCode(ul)); + goto error; + } + + //** Get file size + pmmf->cb = GetFileSize(pmmf->hf, NULL) ; + if (pmmf->cb == 0xFFFFFFFF) { + ul = GetLastError(); //** Get error code + Error("Cannot get file size: %s",StringFromWin32ErrorCode(ul)); + goto error; + } + + //** Create anonymous, read-only file mapping + pmmf->hfm = CreateFileMapping(pmmf->hf,NULL,fdwProtect, 0,0, NULL); + if (!pmmf->hfm) { + ul = GetLastError(); //** Get error code + Error("Cannot create file mapping: %s",StringFromWin32ErrorCode(ul)); + goto error; + } + + //** Map from beginning of file (0,0) for entire length of file (0) + pmmf->pb = MapViewOfFile(pmmf->hfm,fdwAccessMapping, 0,0, 0); + if (!pmmf->pb) { + ul = GetLastError(); //** Get error code + Error("Cannot map view of file: %s",StringFromWin32ErrorCode(ul)); + goto error; + } + + //** Save name in mmf structure + strcpy(pmmf->ach,psz); + + //** Success + return TRUE; + +error: + //** Clean up mmf + + if (pmmf->hfm) { + CloseHandle(pmmf->hfm); + pmmf->hfm = NULL; + } + + if (pmmf->hf) { + CloseHandle(pmmf->hf); + pmmf->hf = NULL; + } + + pmmf->cb = 0; + + return FALSE; +} + + +/*** UnmapFile - Unmap a file mapping created with MapFile() + * + * Entry: + * pmmf - Pointer to structure to receive mapping information + * + * Exit-Success: + * Returns TRUE; mapping destroyed; pmmf zeroed + * + * Exit-Failure: + * Returns FALSE. + */ +BOOL UnmapFile(PMEMORYMAPPEDFILE pmmf) +{ + ULONG ul; + + if (!UnmapViewOfFile(pmmf->pb)) { + ul = GetLastError(); //** Get error code + Error("Cannot unmap view of file: %s",StringFromWin32ErrorCode(ul)); + return FALSE; + } + pmmf->pb = NULL; // Pointer no longer valid + + if (!CloseHandle(pmmf->hfm)) { + ul = GetLastError(); //** Get error code + Error("Cannot destroy file mapping: %s",StringFromWin32ErrorCode(ul)); + return FALSE; + } + pmmf->hfm = NULL; // Handle no longer valid + + if (!CloseHandle(pmmf->hf)) { + ul = GetLastError(); //** Get error code + Error("Cannot close file handle: %s",StringFromWin32ErrorCode(ul)); + return FALSE; + } + pmmf->hf = NULL; // Handle no longer valid + pmmf->ach[0] = '\0'; // Empty file name + pmmf->cb = 0; // File size is unknown + + return TRUE; +} + + +char *StringFromWin32ErrorCode(DWORD error) +{ + static char ach[100]; //BUGBUG 11/11/93 bens Localization! + + switch (error) { + + case -8 : return "LZERROR_UNKNOWNALG"; + case -7 : return "LZERROR_BADVALUE"; + case -6 : return "LZERROR_GLOBLOCK"; + case -5 : return "LZERROR_GLOBALLOC"; + case -4 : return "LZERROR_WRITE"; + case -3 : return "LZERROR_READ"; + case -2 : return "LZERROR_BADOUTHANDLE"; + case -1 : return "LZERROR_BADINHANDLE"; + case 0L : return "NO_ERROR"; + case 1L : return "ERROR_INVALID_FUNCTION"; + case 2L : return "ERROR_FILE_NOT_FOUND"; + case 3L : return "ERROR_PATH_NOT_FOUND"; + case 4L : return "ERROR_TOO_MANY_OPEN_FILES"; + case 5L : return "ERROR_ACCESS_DENIED"; + case 6L : return "ERROR_INVALID_HANDLE"; + case 7L : return "ERROR_ARENA_TRASHED"; + case 8L : return "ERROR_NOT_ENOUGH_MEMORY"; + case 9L : return "ERROR_INVALID_BLOCK"; + case 10L: return "ERROR_BAD_ENVIRONMENT"; + case 11L: return "ERROR_BAD_FORMAT"; + case 12L: return "ERROR_INVALID_ACCESS"; + case 13L: return "ERROR_INVALID_DATA"; + case 14L: return "ERROR_OUTOFMEMORY"; + case 15L: return "ERROR_INVALID_DRIVE"; + case 16L: return "ERROR_CURRENT_DIRECTORY"; + case 17L: return "ERROR_NOT_SAME_DEVICE"; + case 18L: return "ERROR_NO_MORE_FILES"; + case 19L: return "ERROR_WRITE_PROTECT"; + case 20L: return "ERROR_BAD_UNIT"; + case 21L: return "ERROR_NOT_READY"; + case 22L: return "ERROR_BAD_COMMAND"; + case 23L: return "ERROR_CRC"; + case 24L: return "ERROR_BAD_LENGTH"; + case 25L: return "ERROR_SEEK"; + case 26L: return "ERROR_NOT_DOS_DISK"; + case 27L: return "ERROR_SECTOR_NOT_FOUND"; + case 28L: return "ERROR_OUT_OF_PAPER"; + case 29L: return "ERROR_WRITE_FAULT"; + case 30L: return "ERROR_READ_FAULT"; + case 31L: return "ERROR_GEN_FAILURE"; + case 32L: return "ERROR_SHARING_VIOLATION"; + case 33L: return "ERROR_LOCK_VIOLATION"; + case 34L: return "ERROR_WRONG_DISK"; + case 36L: return "ERROR_SHARING_BUFFER_EXCEEDED"; + case 38L: return "ERROR_HANDLE_EOF"; + case 39L: return "ERROR_HANDLE_DISK_FULL"; + case 50L: return "ERROR_NOT_SUPPORTED"; + case 51L: return "ERROR_REM_NOT_LIST"; + default: + sprintf(ach,"Error higher than 51: %d", error); + return ach; + } +} + diff --git a/private/windows/diamond/fileutil.c b/private/windows/diamond/fileutil.c new file mode 100644 index 000000000..699a6c924 --- /dev/null +++ b/private/windows/diamond/fileutil.c @@ -0,0 +1,991 @@ +/*** fileutil.c - Utility routines for dealing with files + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 20-Feb-1994 bens Initial version (code from diamond.c) + * 21-Feb-1994 bens Add IsWildPath() + * 24-Feb-1994 bens Added tempfile functions + * 23-Mar-1994 bens Added Win32<->MS-DOS file attribute mapping + * 03-Jun-1994 bens VER.DLL support + * 07-Jun-1994 bens Move VER.DLL stuff to filever.c + * 14-Dec-1994 bens Fix bug in IsWildPath() + * 02-Feb-1996 msliger Reduced bogosity in pattern matcher + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <malloc.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <errno.h> +#include <direct.h> + +#ifdef BIT16 +#include <dos.h> +#else // !BIT16 +//** Get minimal Win32 definitions +//#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ERROR // Override stupid "#define ERROR 0" in wingdi.h +#endif // !BIT16 + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "fileutil.h" + +#include "fileutil.msg" // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath" + + + +/** TEMPFILE definitions + * + */ +typedef struct { /* tmp */ +#ifdef ASSERT + SIGNATURE sig; // structure signature sigTEMPFILE +#endif + FILE *pfile; // Stream pointer (fopen,fread,fwrite,fclose,...) + char *pszFile; // Constructed filename (MemFree to free) + char *pszDesc; // Description of tempfile +} TEMPFILE; +typedef TEMPFILE *PTEMPFILE; /* ptmp */ + +#ifdef ASSERT +#define sigTEMPFILE MAKESIG('T','M','P','F') // TEMPFILE signature +#define AssertTmp(ptmp) AssertStructure(ptmp,sigTEMPFILE); +#else // !ASSERT +#define AssertTmp(ptmp) +#endif // !ASSERT + + +#define PTMPfromHTMP(htmp) ((PTEMPFILE)(htmp)) +#define HTMPfromPTMP(ptmp) ((HTEMPFILE)(ptmp)) + + + +/*** TmpCreate - Create a temporary file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +HTEMPFILE TmpCreate(char *pszDesc, char *pszPrefix, char *pszMode, PERROR perr) +{ +#define cTMP_RETRY 5 // Number of tempfile retries + int cFailure=0; + FILE *pfile=NULL; + char *pszTmpName; + PTEMPFILE ptmp; + + //** Try to create a temp file (give it 6 tries for good luck) + while (pfile == NULL) { + pszTmpName = _tempnam("",pszPrefix); // Get a name + if (pszTmpName != NULL) { + pfile = fopen(pszTmpName,pszMode); // Create the file + } + if (pfile == NULL) { // Name or create failed + cFailure++; // Count failures + if (cFailure > cTMP_RETRY) { // Failure, select error message + if (pszTmpName == NULL) { // Name create failed + ErrSet(perr,pszFILERR_CANT_CREATE_TMP,"%s",pszDesc); + } + else { // File create failed + ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s", + pszDesc,pszTmpName); + } + free(pszTmpName); + return NULL; + } + free(pszTmpName); + } + } + + //** File create worked, allocate our tempfile structure and fill it in + if (!(ptmp = MemAlloc(sizeof(TEMPFILE)))) { + ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc); + goto error; + } + ptmp->pszFile = NULL; + ptmp->pszDesc = NULL; + if (!(ptmp->pszFile = MemStrDup(pszTmpName))) { + ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc); + goto error; + } + if (!(ptmp->pszDesc = MemStrDup(pszDesc))) { + ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc); + goto error; + } + ptmp->pfile = pfile; + SetAssertSignature(ptmp,sigTEMPFILE); + return HTMPfromPTMP(ptmp); // Success + +error: + if (!ptmp) { + if (ptmp->pszDesc != NULL) { + MemFree(ptmp->pszDesc); + } + if (ptmp->pszFile != NULL) { + MemFree(ptmp->pszFile); + } + MemFree(ptmp); + } + fclose(pfile); + free(pszTmpName); + return NULL; // Failure +} /* createTempFile() */ + + +/*** TmpGetStream - Get FILE* from HTEMPFILE, to perform I/O + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +FILE *TmpGetStream(HTEMPFILE htmp, PERROR perr) +{ + PTEMPFILE ptmp; + + ptmp = PTMPfromHTMP(htmp); + AssertTmp(ptmp); + + return ptmp->pfile; +} /* TmpGetStream() */ + + +/*** TmpGetDescription - Get description of temporary file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +char *TmpGetDescription(HTEMPFILE htmp, PERROR perr) +{ + PTEMPFILE ptmp; + + ptmp = PTMPfromHTMP(htmp); + AssertTmp(ptmp); + + return ptmp->pszDesc; +} /* TmpGetDescription() */ + + +/*** TmpGetFileName - Get filename of temporary file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +char *TmpGetFileName(HTEMPFILE htmp, PERROR perr) +{ + PTEMPFILE ptmp; + + ptmp = PTMPfromHTMP(htmp); + AssertTmp(ptmp); + + return ptmp->pszFile; +} /* TmpGetFileName() */ + + +/*** TmpClose - Close a temporary file stream, but keep tempfile handle + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL TmpClose(HTEMPFILE htmp, PERROR perr) +{ + PTEMPFILE ptmp; + + ptmp = PTMPfromHTMP(htmp); + AssertTmp(ptmp); + + //** Only close if it is open + if (ptmp->pfile != NULL) { + if (fclose(ptmp->pfile) == EOF) { // Close it + ErrSet(perr,pszFILERR_CANT_CLOSE_TMP,ptmp->pszDesc); + return FALSE; + } + ptmp->pfile = NULL; // Remember stream is closed + } + + return TRUE; +} /* TmpClose() */ + + +/*** TmpOpen - Open the stream for a temporary file + * + * NOTE: See fileutil.h for entry/exit conditions. + * Entry: + * htmp - Handle to temp file + * pszMode - Mode string passed to fopen ("wt", "wb", "rt", etc.) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; stream opened + * + * Exit-Failure: + * Returns NULL; perr filled in + */ +BOOL TmpOpen(HTEMPFILE htmp, char *pszMode, PERROR perr) +{ + PTEMPFILE ptmp; + + ptmp = PTMPfromHTMP(htmp); + AssertTmp(ptmp); + + Assert(ptmp->pfile == NULL); // Can't open if already open + ptmp->pfile = fopen(ptmp->pszFile,pszMode); // Open the file + if (!ptmp->pfile) { + ErrSet(perr,pszFILERR_CANNOT_OPEN_TMP,"%s%s", + ptmp->pszDesc,ptmp->pszFile); + } + return (ptmp->pfile != NULL); // Indicate success/failure +} /* TmpOpen() */ + + +/*** TmpDestroy - Delete tempfil and destroy handle + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL TmpDestroy(HTEMPFILE htmp, PERROR perr) +{ + PTEMPFILE ptmp; + + ptmp = PTMPfromHTMP(htmp); + AssertTmp(ptmp); + + //** Make sure file is closed + if (ptmp->pfile != NULL) { + fclose(ptmp->pfile); + } + + //** Delete tempfile + if (remove(ptmp->pszFile) != 0) { + ErrSet(perr,pszFILERR_CANT_DELETE_TMP,"%s%s", + ptmp->pszDesc,ptmp->pszFile); + } + + //** Free Memory + if (ptmp->pszDesc != NULL) { + MemFree(ptmp->pszDesc); + } + if (ptmp->pszFile != NULL) { + MemFree(ptmp->pszFile); + } + ClearAssertSignature(ptmp); + MemFree(ptmp); + + //** Success + return TRUE; +} /* TmpDestroy() */ + + +/*** getFileSize - Get size of a file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +long getFileSize(char *pszFile, PERROR perr) +{ + struct _stat statbuf; // Buffer for _stat() + + //** Get file status + if (_stat(pszFile,&statbuf) == -1) { + //** Could not get file status + ErrSet(perr,pszFILERR_FILE_NOT_FOUND,"%s",pszFile); + return -1; + } + + //** Make sure it is a file + if (statbuf.st_mode & (_S_IFCHR | _S_IFDIR)) { // device or directory + ErrSet(perr,pszFILERR_NOT_A_FILE,"%s",pszFile); + return -1; + } + + //** Success + return statbuf.st_size; +} /* getFileSize() */ + + +/*** appendPathSeparator - Append a path separator only if necessary + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +int appendPathSeparator(char *pszPathEnd) +{ + //** Add path separator if necessary + if ((*pszPathEnd != '\0') && // Path is not empty + (*pszPathEnd != chPATH_SEP1) && // Not a path separator + (*pszPathEnd != chPATH_SEP2) && // Not a path separator + (*pszPathEnd != chDRIVE_SEP) ) { // Not a drive separator + *(++pszPathEnd) = chPATH_SEP1; // Add path separator + *(++pszPathEnd) = '\0'; // Terminate path + return 1; // Account for path separator + } + //** No separator added + return 0; +} /* appendPathSeparator() */ + + +/*** catDirAndFile - Concatenate a possibly empty dir and file name + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL catDirAndFile(char * pszResult, + int cbResult, + char * pszDir, + char * pszFile, + char * pszFileDef, + PERROR perr) +{ + int cch; + char *pch; + +//BUGBUG 14-Feb-1994 bens Need to add pszName to say what field was bad + + //** Handle directory + pszResult[0] = '\0'; // No filespec, yet + cch = strlen(pszDir); // Get length of dir + if (cch != 0) { // Have to concatenate path + strcpy(pszResult,pszDir); // Copy destination dir to buffer + cbResult -= cch; // Account for dir + //** Add path separator if necessary, adjust remaining size + cbResult -= appendPathSeparator(&(pszResult[cch-1])); + if (cbResult <= 0) { + ErrSet(perr,pszFILERR_PATH_TOO_LONG,"%s",pszDir); + return FALSE; + } + } + + //** Append file name, using default if primary one not supplied + if (*pszFile == '\0') { // Need to construct file name + if (*pszFileDef == '\0') { // Empty default name, too + return TRUE; // We're done! + } + pch = getJustFileNameAndExt(pszFileDef,perr); // Get default name + if (pch == NULL) { + return FALSE; // perr already filled in + } + } + else { + pch = pszFile; // Use supplied name + } + strcat(pszResult,pch); // Append file name + cbResult -= strlen(pch); // Update remaining size + if (cbResult <= 0) { + ErrSet(perr,pszFILERR_PATH_TOO_LONG,"%s",pch); + return FALSE; + } + + //** Success + return TRUE; +} /* catDirAndFile() */ + + +/*** ensureDirectory - Ensure directory exists (creating as needed) + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL ensureDirectory(char *pszPath, BOOL fHasFileName, PERROR perr) +{ + char achDir[cbFILE_NAME_MAX]; // Partial directory buffer + int cErrors; + int cch; + int cchNoPathSep; + int i; // Temp file name count + int fh; // File handle + char *pch; + char *pchCurr; // Current path separator + char *pchNext; // Next path separator + + //** Find first path separator, if any. + // NOTE: Have to handle case of "d:foo" specially! + // + for (pch=pszPath; + *pch && // Not end of string + (*pch!=chPATH_SEP1) && // Not path separator 1 + (*pch!=chPATH_SEP2) && // Not path separator 2 + ((*pch!=chDRIVE_SEP) || ((*(pch+1)==chPATH_SEP1) || // Not "d:\" + (*(pch+1)==chPATH_SEP2))); + pch++) { + ; // + } + + //** Set correct starting point for first directory component (if any) + achDir[0] = '\0'; // Assume current directory + if ((*pch == '\0') && // No path separators + fHasFileName) { // Just a file name + //** Do nothing; for loop below will be skipped because *pch == \0 + } + else { + //** Have to consider whole path + pch = pszPath; // Need to ensure directories + } + + //** Make sure directories on path exist (create them all) + // We need to identify successive components and create directory + // tree one component at a time. Since the directory may already + // exist, we do the final test of creating a file there to make + // sure we can do the write. + + for (pchCurr=pch, pchNext=pch; // Process path components + *pchCurr && *pchNext; // Until no more + pchCurr=pchNext+1) { // Skip over last path separator + //** Find next path separator + for (pch=pchCurr; + *pch && + (*pch!=chPATH_SEP1) && + (*pch!=chPATH_SEP2) && + ((*pch!=chDRIVE_SEP) || ((*(pch+1)==chPATH_SEP1) || // Not "d:\" + (*(pch+1)==chPATH_SEP2))); + pch++) { + ; // + } + pchNext = pch; // Location of next path separator + + //** Don't process last component if caller said it was a filename + if ((*pchNext != '\0') || !fHasFileName) { + //** We have a partial path; make sure directory exists + cch = pchNext-pszPath; // Length of partial path + if ((cch>0) && + ((*pchNext == chDRIVE_SEP) || (*(pchNext-1) == chDRIVE_SEP))) { + //** Have "d:xxx" or "d:\xxx", so need to include ":" or "\"! + cch++; + } + strncpy(achDir,pszPath,cch); + achDir[cch] = '\0'; // Terminate path + _mkdir(achDir); // Ignore any error + } + } + + //** Check for special case of root directory: "\" or "\xxx.yyy" + if ((strlen(achDir) == 0) && + (strlen(pszPath) > 0) && + ((*pszPath == chPATH_SEP1) || (*pszPath == chPATH_SEP2))) { + achDir[0] = *pszPath; + achDir[1] = '\0'; + } + + //** Make sure there is an appropriate separator + cch = strlen(achDir); + cchNoPathSep = cch; // For error reporting + cch += appendPathSeparator(&(achDir[cch-1])); + + //** Make sure we can write to the directory + // achDir = Has path of directory to test + cErrors = 0; // No errors, so far + for (i=0; i<999; i++) { + //** Form full file name + sprintf(&achDir[cch],"DIA%d.TMP",i); + + //** Make sure file does not exist, and can be created and written to + fh = _open(achDir, + _O_CREAT | _O_EXCL | _O_RDWR, // Must not exist, read/write + _S_IREAD | _S_IWRITE); // Read & Write permission + + //** Figure out what happened + if (fh == -1) { + switch (errno) { + case EACCES: // Was a dir, or read-only + cErrors++; + if (cErrors < 5) { // Tolerate this a few times + continue; // Try next temp file name + } + achDir[cchNoPathSep] = '\0'; // Remove temp file name + ErrSet(perr,pszFILERR_DIR_NOT_WRITEABLE,"%s",achDir); + return FALSE; + + case EEXIST: // File already exists -- good sign! + continue; // Try next temp file name + + case EMFILE: // Out of file handles + achDir[cchNoPathSep] = '\0'; // Remove temp file name + ErrSet(perr,pszFILERR_NO_MORE_FILE_HANDLES,"%s",achDir); + return FALSE; + + case ENOENT: // File/Path not found + case EINVAL: // oflag and/or pmode args are bad + default: + achDir[cchNoPathSep] = '\0'; // Remove temp file name + ErrSet(perr,pszFILERR_CANT_MAKE_DIR,"%s",achDir); + return FALSE; + } + } + + //** File was created, close it, delete it, and we're golden + _close(fh); // Done with file + _unlink(achDir); // Get rid of it + return TRUE; // Success + } + + //** Ran out of temp file names + achDir[cchNoPathSep] = '\0'; // Remove temp file name + ErrSet(perr,pszFILERR_OUT_OF_TMP_FILE_NAMES,"%d%s",i,achDir); + return FALSE; +} /* ensureDirectory() */ + + +/*** ensureFile - Ensure a file can be created + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL ensureFile(char *pszFile, char *pszDesc, PERROR perr) +{ + int fh; + //** Make sure directory is present + if (!ensureDirectory(pszFile,TRUE,perr)) { + //** Override error message with more meaningful one + ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",pszDesc,pszFile); + return FALSE; + } + + //** Make sure file can be created + fh = _open(pszFile, + _O_CREAT | _O_RDWR, // Create if necessary, read/write + _S_IREAD | _S_IWRITE); // Read & Write permission + if (fh == -1) { + switch (errno) { + case EMFILE: // Out of file handles + ErrSet(perr,pszFILERR_NO_MORE_FILE_HANDLES,"%s",pszFile); + return FALSE; + + case EACCES: // Was a dir, or read-only + case ENOENT: // File/Path not found + case EINVAL: // oflag and/or pmode args are bad + default: + ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",pszDesc,pszFile); + return FALSE; + } + } + + //** File was created; close it, delete it, and we're golden + _close(fh); // Done with file + _unlink(pszFile); // Get rid of it + + return TRUE; +} /* ensureFile() */ + + +/*** getJustFileNameAndExt - Get last component in filespec + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +char *getJustFileNameAndExt(char *pszPath, PERROR perr) +{ + char *pch=pszPath; + char *pchStart=pszPath; // Assume filespec is just a name[.ext] + + //** Find last path separator + while (*pch) { + switch (*pch) { + case chPATH_SEP1: + case chPATH_SEP2: + case chDRIVE_SEP: + pchStart = pch+1; // Name starts after path/drive separator + break; + } + pch++; // Check next character + } + + //** Make sure file name is not empty + if (*pchStart == '\0') { // Empty file name + ErrSet(perr,pszFILERR_EMPTY_FILE_NAME,"%s",pszPath); + return NULL; // Failure + } + else { + return pchStart; // Success + } +} /* getJustFileNameAndExt() */ + + +/*** IsWildMatch - Test filespec against wild card specification + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL IsWildMatch(char *pszPath, char *pszWild, PERROR perr) +{ + char chNext; + char *psz; + char *psz1; // Walks through filespec + char *psz2; // Walks through pattern + + + psz1 = pszPath; // Filespec to test + psz2 = pszWild; // Test pattern + + + // While there is pattern to account for keep going + while (*psz2) { + switch (*psz2) { // Handle wild card chars in pattern + + case chWILD_RUN: + //** Find next non-wildcard character => treat run of */? as 1 * + for (psz=psz2+1; + (*psz == chWILD_RUN) || (*psz == chWILD_CHAR); + psz++) { + ; //** Skip through pattern string + } + //** *psz is either EOL, or not a wildcard + chNext = *psz; // Character to terminate run + //** Span until run terminates -- either + while ((*psz1 != '\0') && // Don't run off filespec + (*psz1 != chNext) && // Stop at run terminator + (*psz1 != chNAME_EXT_SEP)) { // "." not allowed to match + psz1++; + } + //** At this point, we've matched as much as we could; + // If there is a failure, the next iteration through the + // loop will find it; So, just update the pattern position. + psz2 = psz; + break; + + case chWILD_CHAR: + if (*psz1 == chNAME_EXT_SEP) { // Match anything but "." + return FALSE; // Found point of mismatch + } + if (*psz1) + psz1++; // Next position in filespec + psz2++; // Next position in pattern + break; + + case chNAME_EXT_SEP: + if (*psz1 == chNAME_EXT_SEP) { + psz1++; + psz2++; + } else if (*psz1 == '\0') { + psz2++; + } else { + return FALSE; + } + break; + + default: + if (toupper(*psz1) != toupper(*psz2)) { // Still match + return FALSE; // Found point of mismatch + } + if (*psz1) + psz1++; // Next position in filespec + psz2++; // Next position in pattern + break; + } + } + + //** We have a match if *both* strings were fully consumed + return ((*psz1 == '\0') && (*psz2 == '\0')); +} /* IsWildMatch() */ + + +#pragma optimize("",off) // Avoid optimizer warning on in-line ASM +/*** IsPathRemovable - See if path refers to a removable media drive + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL IsPathRemovable(char *pszPath, char *pchDrive) +{ + char ach[4]="x:\\"; // Buffer for "x:\" + BOOL fRemovable; + char iDrive; + + //** Get drive for path + if ((strlen(pszPath) >= 2) && + isalpha(pszPath[0]) && + (pszPath[1] == chDRIVE_SEP)) { + iDrive = toupper(pszPath[0]) - 'A' + 1; + } + else { + iDrive = _getdrive(); + } + *pchDrive = 'A' + iDrive - 1; // Return drive letter + +#ifdef BIT16 + //** Do it the MS-DOS way + _asm { + mov fRemovable,0 ; Assume not removable + mov bl,iDrive ; (0=default; 1=A, ...) + mov ax,4408h ; IOCTL Get Removable Media + int 21h ; Call MS-DOS + jc not_removable ; Error, assume not removable + + or ax,ax ; Test removability flag + jne not_removable + + mov fRemovable,1 ; Drive is removable + + not_removable: + } +#else // !BIT16 + //** Do it the Win32 way + ach[0] = *pchDrive; // Construct path to root of drive to test + fRemovable = GetDriveType(ach) == DRIVE_REMOVABLE; +#endif + return fRemovable; // Return removability +} /* IsPathRemovable() */ +#pragma optimize("",on) // Restore previous Optimization settings + + +/*** GetFileTimeAndAttr - Get date, time, and attributes from a file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL GetFileTimeAndAttr(PFILETIMEATTR pfta, char *pszFile, PERROR perr) +{ +#ifdef BIT16 + //** Do it the MS-DOS way + int hf; + + hf = _open(pszFile, _O_RDONLY | _O_BINARY); + if (hf == -1) { + ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); + return FALSE; + } +//BUGBUG 30-Mar-1994 bens Ignore errors??? + _dos_getftime(hf,&pfta->date,&pfta->time); + _close(hf); + _dos_getfileattr(pszFile,&pfta->attr); + return TRUE; + +#else // !BIT16 + //** Do it the Win32 way + BOOL rc; + FILETIME ft; + FILETIME ftUTC; // Win32 returns stupid Universal Time Code + HANDLE hfQuery; + + hfQuery = CreateFile(pszFile, // open again with Win32 + GENERIC_READ, // Just to read + FILE_SHARE_READ,// Coexist with previous open + NULL, // No security + OPEN_EXISTING, // Must exist + 0L, // We're not setting any attributes + NULL); // No template handle + if (hfQuery == INVALID_HANDLE_VALUE) { + ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); + return FALSE; + } + + //** Get date/time and convert it + rc = GetFileTime(hfQuery,NULL,NULL,&ftUTC); + rc |= FileTimeToLocalFileTime(&ftUTC,&ft); // Apply timezone + rc |= FileTimeToDosDateTime(&ft,&pfta->date,&pfta->time); + CloseHandle(hfQuery); + + //** Get attributes and convert them + pfta->attr = AttrFATFromAttr32(GetFileAttributes(pszFile)); + if (!rc) { + ErrSet(perr,pszFILERR_CANNOT_GET_FILE_INFO,"%s",pszFile); + return FALSE; + } + return TRUE; +#endif +} /* GetFileTimeAndAttr() */ + + +/*** SetFileTimeAndAttr - Set date, time, and attributes of a file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL SetFileTimeAndAttr(char *pszFile, PFILETIMEATTR pfta, PERROR perr) +{ +#ifdef BIT16 + //** Do it the MS-DOS way + + int hf; + + hf = _open(pszFile,_O_WRONLY | _O_BINARY); + if (hf == -1) { + ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); + return FALSE; + } + + _dos_setftime(hf,pfta->date,pfta->time); + _close(hf); + _dos_setfileattr(pszFile,pfta->attr); + return TRUE; + +#else // !BIT16 + //** Do it the Win32 way + HANDLE hfSet; + FILETIME ft; + FILETIME ftUTC; // Win32 needs stupid Universal Time Code + BOOL rc; + + hfSet = CreateFile(pszFile, // open with Win32 + GENERIC_WRITE, // Need to be able to modify properties + 0, // Deny all + NULL, // No security + OPEN_EXISTING, // Must exist + 0L, // We're not setting any attributes + NULL); // No template handle + if (hfSet == INVALID_HANDLE_VALUE) { + ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); + return FALSE; + } + + rc = DosDateTimeToFileTime(pfta->date,pfta->time,&ft); + rc |= LocalFileTimeToFileTime(&ft, &ftUTC); // Apply timezone + rc |= SetFileTime(hfSet,NULL,NULL,&ftUTC); + rc |= SetFileAttributes(pszFile,Attr32FromAttrFAT(pfta->attr)); + CloseHandle(hfSet); + if (!rc) { + ErrSet(perr,pszFILERR_CANNOT_SET_FILE_INFO,"%s",pszFile); + return FALSE; + } + return TRUE; +#endif // !BIT16 +} /* SetFileTimeAndAttr() */ + + +/*** CopyOneFile - Make a faithful copy of a file + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL CopyOneFile(char *pszDst, + char *pszSrc, + BOOL fCopy, + UINT cbBuffer, + PFNOVERRIDEFILEPROPERTIES pfnofp, + void *pv, + PERROR perr) +{ + UINT cbRead; + UINT cbWritten; + BOOL fSuccess = FALSE; // Assume failure + FILETIMEATTR fta; + int hfDst = -1; + int hfSrc = -1; + char *pbuf = NULL; + + //** Open source + hfSrc = _open(pszSrc, _O_RDONLY | _O_BINARY); + if (hfSrc == -1) { + ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszSrc); + goto cleanup; + } + + //** Get file date, time, and attributes for source file + if (!GetFileTimeAndAttr(&fta,pszSrc,perr)) { + goto cleanup; + } + + //** Permit caller to override date/time/attr + if (pfnofp != NULL) { + if (!(*pfnofp)(&fta,pv,perr)) { // Call override function + goto cleanup; // Error, go cleanup + } + } + + //** Early out if we were just merging file date/time/attr values + if (!fCopy) { + fSuccess = TRUE; // Success + goto cleanup; // Go close source and exit + } + + //** Get copy buffer + if (!(pbuf = MemAlloc(cbBuffer))) { + ErrSet(perr,pszFILERR_NO_MEMORY_FOR_BUFFER,"%s%s",pszSrc,pszDst); + goto cleanup; + } + + //** Open destination + hfDst = _open(pszDst, + _O_BINARY | _O_RDWR | _O_CREAT, // No translation, R/W + _S_IREAD | _S_IWRITE); // Attributes when file is closed + if (hfDst == -1) { + ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszDst); + goto cleanup; + } + + //** Copy data + while (!_eof(hfSrc)) { + //** Read chunk + cbRead = _read(hfSrc,pbuf,cbBuffer); + if (cbRead == -1) { + ErrSet(perr,pszFILERR_READ_FILE,"%s",pszSrc); + goto cleanup; + } + else if (cbRead != 0) { // Not at EOF + //** Write it + cbWritten = _write(hfDst,pbuf,cbRead); + if (cbWritten != cbRead) { + ErrSet(perr,pszFILERR_WRITE_FILE,"%s",pszSrc); + goto cleanup; + } + } + } + //** Done copying, close destination file handle + _close(hfDst); + hfDst = -1; // Avoid unnecessary close in cleanup + + //** Set file date, time, and attributes + if (!SetFileTimeAndAttr(pszDst,&fta,perr)) { + goto cleanup; + } + + //** Success! + fSuccess = TRUE; + +cleanup: + if (hfDst != -1) { + _close(hfDst); + } + if (hfSrc != -1) { + _close(hfSrc); + } + if (pbuf) { + MemFree(pbuf); + } + + return fSuccess; +} /* CopyOneFile() */ + + +#ifndef BIT16 +//** Win32 stuff + +/*** Attr32FromAttrFAT - Convert FAT file attributes to Win32 form + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +DWORD Attr32FromAttrFAT(WORD attrMSDOS) +{ + //** Quick out for normal file special case + if (attrMSDOS == _A_NORMAL) { + return FILE_ATTRIBUTE_NORMAL; + } + + //** Otherwise, mask off read-only, hidden, system, and archive bits + // NOTE: These bits are in the same places in MS-DOS and Win32! + // + Assert(_A_RDONLY == FILE_ATTRIBUTE_READONLY); + Assert(_A_HIDDEN == FILE_ATTRIBUTE_HIDDEN); + Assert(_A_SYSTEM == FILE_ATTRIBUTE_SYSTEM); + Assert(_A_ARCH == FILE_ATTRIBUTE_ARCHIVE); + return attrMSDOS & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); +} + + +/*** AttrFATFromAttr32 - Convert Win32 file attributes to FAT form + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +WORD AttrFATFromAttr32(DWORD attr32) +{ + //** Quick out for normal file special case + if (attr32 & FILE_ATTRIBUTE_NORMAL) { + return _A_NORMAL; + } + + //** Otherwise, mask off read-only, hidden, system, and archive bits + // NOTE: These bits are in the same places in MS-DOS and Win32! + // + Assert(_A_RDONLY == FILE_ATTRIBUTE_READONLY); + Assert(_A_HIDDEN == FILE_ATTRIBUTE_HIDDEN); + Assert(_A_SYSTEM == FILE_ATTRIBUTE_SYSTEM); + Assert(_A_ARCH == FILE_ATTRIBUTE_ARCHIVE); + return ((WORD)attr32) & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); +} + +#endif // !BIT16 diff --git a/private/windows/diamond/fileutil.h b/private/windows/diamond/fileutil.h new file mode 100644 index 000000000..4616f852c --- /dev/null +++ b/private/windows/diamond/fileutil.h @@ -0,0 +1,448 @@ +/*** fileutil.h - Utility routines for dealing with files + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 20-Feb-1994 bens Initial version (code from diamond.c) + * 23-Feb-1994 bens Added createTempFile() + * 23-Feb-1994 bens Added richer tempfile routines + * 23-Mar-1994 bens Added Win32<->MS-DOS file attribute mapping + * 03-Jun-1994 bens VER.DLL support + * 07-Jun-1994 bens Move VER.DLL stuff to filever.c + * 14-Dec-1994 bens Update list of exported functions + * + * Exported Functions: + * CopyOneFile - Make a faithful copy of a file + * getFileSize - Get size of a file + * GetFileTimeAndAttr - Get date, time, and attributes from a file + * SetFileTimeAndAttr - Set date, time, and attributes of a file + * appendPathSeparator - Append a path separator only if necessary + * catDirAndFile - Concatenate a possibly empty dir and file name + * ensureDirectory - Ensure directory exists (creating as needed) + * ensureFile - Ensure a file can be created + * getJustFileNameAndExt - Get last component in filespec + * Attr32FromAttrFAT - Convert FAT file attributes to Win32 form + * AttrFATFromAttr32 - Convert Win32 file attributes to FAT form + * IsWildMatch - Test filespec against wild card specification + * IsPathRemovable - See if path refers to a removable media drive + * TmpCreate - Create a temporary file + * TmpGetStream - Get FILE* from HTEMPFILE, to perform I/O + * TmpGetDescription - Get description of temporary file + * TmpGetFileName - Get filename of temporary file + * TmpClose - Close temporary file, but keep tempfile handle + * TmpOpen - Open the stream for a temporary file + * TmpDestroy - Delete tempfil and destroy handle + */ + +#ifndef INCLUDED_FILEUTIL +#define INCLUDED_FILEUTIL 1 + +#include "error.h" +#include <stdio.h> + +typedef void *HTEMPFILE; + +//** Maximum path length +#define cbFILE_NAME_MAX 256 // Maximum filespec length + +#define pszALL_FILES "*.*" // Match all files + +//** File name characters & wild card characters +#define chPATH_SEP1 '\\' // Character to separate file path components +#define chPATH_SEP2 '/' // Character to separate file path components + // Ex: one<\>two<\>foo.dat + +#define chNAME_EXT_SEP '.' // Character that separates file name and ext + // Ex: one\two\foo<.>dat + +#define chDRIVE_SEP ':' // Character that separates drive letter + // Ex: a<:>\foo.dat + +#define chWILD_RUN '*' // Wild card character that matches a run + +#define chWILD_CHAR '?' // Wild card character that matches single char + + +/*** FILETIMEATTR - Lowest common denominator file date/time/attributes + * + * The format of these match the MS-DOS FAT file system. + */ +typedef struct { + USHORT date; // file date + USHORT time; // file time + USHORT attr; // file attibutes +} FILETIMEATTR; /* fta */ +typedef FILETIMEATTR *PFILETIMEATTR; /* pfta */ + + +/*** PFNOVERRIDEFILEPROPERTIES - Function type for CopyOneFile override + *** FNOVERRIDEFILEPROPERTIES - macro to help define CopyOneFile override + * + * Entry: + * pfta - File date/time/attr structure + * pv - Client context pointer + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, pfta structure may have been modified. + * + * Exit-Failure: + * Returns FALSE, + * ERROR structure filled in with details of error. + */ +typedef BOOL (*PFNOVERRIDEFILEPROPERTIES)(PFILETIMEATTR pfta, /* pfnofp */ + void *pv, + PERROR perr); +#define FNOVERRIDEFILEPROPERTIES(fn) BOOL fn(PFILETIMEATTR pfta, \ + void *pv, \ + PERROR perr) + + +/*** CopyOneFile - Make a faithful copy of a file + * + * Entry: + * pszDst - Name of destination file + * pszSrc - Name of source file + * fCopy - TRUE => copy file; FALSE => open src, call pfnofp to + * merge file date/time/attr values, but skip COPY! + * cbBuffer - Amount of temporary buffer space to use for copying + * pfnofp - Function to override file properties; called with the + * file date/time/attributes for pszSrc to permit client + * to override these values. Pass NULL if no override + * desired. + * pv - Client context pointer + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; file copied successfully + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL CopyOneFile(char *pszDst, + char *pszSrc, + BOOL fCopy, + UINT cbBuffer, + PFNOVERRIDEFILEPROPERTIES pfnofp, + void *pv, + PERROR perr); + + +/*** getFileSize - Get size of a file + * + * Entry: + * pszFile - Filespec + * perr - ERROR structure + * + * Exit-Success: + * Returns size of file. + * + * Exit-Failure: + * Returns -1; perr filled in with error. + */ +long getFileSize(char *pszFile, PERROR perr); + + +/*** GetFileTimeAndAttr - Get date, time, and attributes from a file + * + * Entry: + * pfta - Structure to receive date, time, and attributes + * pszFile - Name of file to inspect + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pfta filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL GetFileTimeAndAttr(PFILETIMEATTR pfta, char *pszFile, PERROR perr); + + +/*** SetFileTimeAndAttr - Set date, time, and attributes of a file + * + * Entry: + * pszFile - Name of file to modify + * pfta - Structure to receive date, time, and attributes + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pfta filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL SetFileTimeAndAttr(char *pszFile, PFILETIMEATTR pfta, PERROR perr); + + +/*** appendPathSeparator - Append a path separator only if necessary + * + * Entry: + * pszPathEnd - Pointer to last character in a path string + * (will be NULL byte if path is empty). + * Buffer is assumed to have room for one more character! + * + * Exit: + * Returns 1 if a path separator was appended + * Returns 0 if no path separator was appended: + * 1) Path was empty -or- + * 2) Last character of path was '\', '/', or ':' + */ +int appendPathSeparator(char *pszPathEnd); + + +/*** catDirAndFile - Concatenate a possibly empty dir and file name + * + * Note: pszFile/pszFileDef can actually have path characters, and do not + * need to be file names. Essentially, this function just concatenates + * two strings together, ensuring a path separator is between them! + * + * Entry: + * pszResult - Buffer to receive concatentation + * cbResult - Size of pszResult + * pszDir - Possibly empty directory string + * pszFile - Possibly empty file string + * pszFileDef - Path string to use if pszFile is empty; if NULL, then + * pszFile must NOT be empty. If not NULL, and pszFile + * is empty, then pszFileDef is examined, any path + * prefixes are removed, and the base filename.ext + * is used. + * perr - ERROR structure to fill in + * + * Exit-Success: + * Returns TRUE; pszResult filled in. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL catDirAndFile(char * pszResult, + int cbResult, + char * pszDir, + char * pszFile, + char * pszFileDef, + PERROR perr); + + +/*** ensureDirectory - Ensure directory exists (creating as needed) + * + * Entry: + * pszPath - File spec with directory names to ensure exist + * fHasFileName - TRUE if pszPath has file name at end. In this case + * the last component of pszPath is ignored. + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; directory exists + * + * Exit-Failure: + * Returns FALSE; could not create directory, perr filled in with error. + */ +BOOL ensureDirectory(char *pszPath, BOOL fHasFileName, PERROR perr); + + +/*** ensureFile - Ensure a file can be created + * + * Creates any directories that are needed, then creates file and deletes it. + * + * Entry: + * pszPath - File spec with directory names to ensure exist + * pszDesc - Description of type of file (for error message). + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; file can be created + * + * Exit-Failure: + * Returns FALSE; could not create file, perr filled in with error. + */ +BOOL ensureFile(char *pszFile, char *pszDesc, PERROR perr); + + +/*** getJustFileNameAndExt - Get last component in filespec + * + * Entry: + * pszPath - Filespec to parse + * perr - ERROR structure to fill in + * + * Exit-Success: + * Returns pointer into pszPath where last component starts + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +char *getJustFileNameAndExt(char *pszPath, PERROR perr); + + +/*** Attr32FromAttrFAT - Convert FAT file attributes to Win32 form + * + * Entry: + * attrMSDOS - MS-DOS (FAT file system) file attributes + * + * Exit: + * Returns equivalent attributes in Win32 format. + */ +DWORD Attr32FromAttrFAT(WORD attrMSDOS); + + +/*** AttrFATFromAttr32 - Convert Win32 file attributes to FAT form + * + * Entry: + * attrMSDOS - MS-DOS (FAT file system) file attributes + * + * Exit: + * Returns equivalent attributes in Win32 format. + */ +WORD AttrFATFromAttr32(DWORD attr32); + + +/*** IsWildMatch - Test filespec against wild card specification + * + * Entry: + * pszPath - Filespec to test; Must not have path characters -- use + * getJustFileNameAndExt() to get rid of them. + * pszWild - Pattern to test against (may have wild cards) + * perr - ERROR structure to fill in + * + * Exit-Success: + * Returns TRUE; pszPath matches pszWild + * + * Exit-Failure: + * Returns FALSE; no match; use ErrIsError(perr) to see if error + * occured. + */ +BOOL IsWildMatch(char *pszPath, char *pszWild, PERROR perr); + + +/*** IsPathRemovable - See if path refers to a removable media drive + * + * Entry: + * pszPath - Path to test + * pchDrive - Pointer to character to receive drive letter + * + * Exit-Success: + * Returns TRUE; path refers to removable media + * + * Exit-Failure: + * Returns FALSE; path is not removable + * occured. + */ +BOOL IsPathRemovable(char *pszPath, char *pchDrive); + + +/*** TmpCreate - Create a temporary file + * + * Entry: + * pszDesc - Description of temp file (for error reporting) + * pszPrefix - Filename prefix + * pszMode - Mode string passed to fopen ("wt", "wb", "rt", etc.) + * perr - ERROR structure + * + * Exit-Success: + * Returns non-null HTEMPFILE; temp file created and open + * + * Exit-Failure: + * Returns NULL; perr filled in + */ +HTEMPFILE TmpCreate(char *pszDesc, char *pszPrefix, char *pszMode, PERROR perr); + + +/*** TmpGetStream - Get FILE* from HTEMPFILE, to perform I/O + * + * Entry: + * htmp - Handle to temp file + * perr - ERROR structure + * + * Exit-Success: + * Returns non-null FILE* + * + * Exit-Failure: + * Returns NULL; If ErrIsError(perr) indicates no error, then the + * stream had simply been closed with TmpClose(). Else, perr has + * error details. + */ +FILE *TmpGetStream(HTEMPFILE htmp, PERROR perr); + + +/*** TmpGetDescription - Get description of temporary file + * + * Entry: + * htmp - Handle to temp file + * perr - ERROR structure + * + * Exit-Success: + * Returns non-null pointer to description + * + * Exit-Failure: + * Returns NULL; perr filled in + */ +char *TmpGetDescription(HTEMPFILE htmp, PERROR perr); + + +/*** TmpGetFileName - Get filename of temporary file + * + * Entry: + * htmp - Handle to temp file + * perr - ERROR structure + * + * Exit-Success: + * Returns non-null pointer to temp filename + * + * Exit-Failure: + * Returns NULL; perr filled in + */ +char *TmpGetFileName(HTEMPFILE htmp, PERROR perr); + + +/*** TmpClose - Close a temporary file stream, but keep tempfile handle + * + * Entry: + * htmp - Handle to temp file; + * NOTE: This call is a NOP if the stream is already closed. + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; stream closed + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL TmpClose(HTEMPFILE htmp, PERROR perr); + + +/*** TmpOpen - Open the stream for a temporary file + * + * Entry: + * htmp - Handle to temp file + * pszMode - Mode string passed to fopen ("wt", "wb", "rt", etc.) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; stream opened + * + * Exit-Failure: + * Returns NULL; perr filled in + */ +BOOL TmpOpen(HTEMPFILE htmp, char *pszMode, PERROR perr); + + +/*** TmpDestroy - Delete tempfil and destroy handle + * + * Entry: + * htmp - Handle to temp file + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; tempfile destroyed + * + * Exit-Failure: + * Returns NULL; perr filled in + */ +BOOL TmpDestroy(HTEMPFILE htmp, PERROR perr); + + +#endif // !INCLUDED_FILEUTIL diff --git a/private/windows/diamond/fileutil.msg b/private/windows/diamond/fileutil.msg new file mode 100644 index 000000000..00a5408c1 --- /dev/null +++ b/private/windows/diamond/fileutil.msg @@ -0,0 +1,225 @@ +/*** fileutil.msg - fileutil.c displayable strings + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 20-Feb-1994 bens Initial version (moved from diamond.msg) + * 24-Feb-1994 bens Messages for tempfile functions + * 30-Mar-1994 bens CopyFile error messages + * 03-Jun-1994 bens VER.DLL error messages + * 09-Jun-1994 bens Add localization comments (whew!) + */ + +//** Error messages + + /*LOCALIZE + * + * Purpose: Indicates a file could not be found. + * %1 = file name + * Generate: Difficult -- Should never happen. + * Expect: "Could not find file: foo.bar" + */ +#define pszFILERR_FILE_NOT_FOUND "Could not find file: %1" + + + /*LOCALIZE + * + * Purpose: Indicates a filename refers to a Directory or Device + * %1 = file name + * Generate: DO NOT TRY TO REPRO -- not reachable by EXTRACT. + * Expect: "foo.bar is not a file." + */ +#define pszFILERR_NOT_A_FILE "%1 is not a file" + + + /*LOCALIZE + * + * Purpose: Indicates a filespec is too long. + * %1 = file name + * Generate: Type "EXTRACT /L 1234567890.....1234567890 /a loctest1.cab" + * where "123...890" is 253 bytes long (can't do this under + * MS-DOS or Win3.x, have to try NT). + * Expect: "File name too long: 123....890\a.asc" + */ +#define pszFILERR_PATH_TOO_LONG "File name too long: %1" + + + /*LOCALIZE + * + * Purpose: Indicates a failure creating a file in a directory. + * %1 = directory name + * Generate: Connect drive X: to a read-only share (\\productss\release, + * for example), and type "EXTRACT /L x:\ /E loctest1.cab". + * Expect: "Could not create file in directory: x:\" + */ +#define pszFILERR_DIR_NOT_WRITEABLE "Could not create file in directory: %1" + + + /*LOCALIZE + * + * Purpose: Out of file handles trying to make sure directory exists. + * %1 = directory name + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "No more file handles: x:\foo" + */ +#define pszFILERR_NO_MORE_FILE_HANDLES "No more file handles: %1" + + + /*LOCALIZE + * + * Purpose: Path not found on file creation. + * %1 = directory name + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "Path is invalid: x:\foo" + */ +#define pszFILERR_CANT_MAKE_DIR "Path is invalid: %1" + + + /*LOCALIZE + * + * Purpose: Could not find a unique temporary file name. + * %1 = number of different temp file names tried + * %2 = directory name + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "Ran out of temp file names after 999 attempts: x:\foo" + */ +#define pszFILERR_OUT_OF_TMP_FILE_NAMES "Ran out of temp file names after %1 attempts: %2" + + + /*LOCALIZE + * + * Purpose: Empty file name specified in path + * %1 = filespec + * Generate: Difficult -- DO NOT TRY TO REPRO. + * Expect: "Missing file name: x:\foo\" + */ +#define pszFILERR_EMPTY_FILE_NAME "Missing file name: %1" + + + /*LOCALIZE + * + * Purpose: Cannot create a temporary file. + * %1 = description + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot create <description>" + * ^^^^^^^^^^^^^^------------- + */ +#define pszFILERR_CANT_CREATE_TMP "Cannot create %1" + + + /*LOCALIZE + * + * Purpose: Cannot create a temporary file. + * %1 = description + * %2 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot create <description>: <filespec> + */ +#define pszFILERR_CANT_CREATE_FILE "Cannot create %1: %2" + + + /*LOCALIZE + * + * Purpose: Out of memory creating a temporary file. + * %1 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Out of memorying creating <filespec>" + */ +#define pszFILERR_OUT_OF_MEMORY "Out of memorying creating %1" + + + /*LOCALIZE + * + * Purpose: Cannot open specified temporary file. + * %1 = description + * %2 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot open <description>: <filespec>" + */ +#define pszFILERR_CANNOT_OPEN_TMP "Cannot open %1: %2" + + + /*LOCALIZE + * + * Purpose: Cannot close specified temporary file. + * %1 = description + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot close <description>" + */ +#define pszFILERR_CANT_CLOSE_TMP "Cannot close %1" + + + /*LOCALIZE + * + * Purpose: Cannot delete specified temporary file. + * %1 = description + * %2 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot delete <description>: <filespec>" + */ +#define pszFILERR_CANT_DELETE_TMP "Cannot delete %1: %2" + + + /*LOCALIZE + * + * Purpose: Cannot allocate buffer to copy a file. + * %1 = source filespec + * %2 = destination filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Could not allocate buffer to copy <src> to <dst>" + */ +#define pszFILERR_NO_MEMORY_FOR_BUFFER "Could not allocate buffer to copy %1 to %2" + + + /*LOCALIZE + * + * Purpose: Cannot open source or destination for file copy. + * %1 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot open file: <filespec>" + */ +#define pszFILERR_OPEN_FAILED "Cannot open file: %1" + + + /*LOCALIZE + * + * Purpose: Cannot get information about a file for file copy. + * %1 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot get date/time/attributes from file: <filespec>" + */ +#define pszFILERR_CANNOT_GET_FILE_INFO "Cannot get date/time/attributes from file: %1" + + + /*LOCALIZE + * + * Purpose: Cannot set information about a file for file copy. + * %1 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot set date/time/attributes from file: <filespec>" + */ +#define pszFILERR_CANNOT_SET_FILE_INFO "Cannot set date/time/attributes for file: %1" + + + /*LOCALIZE + * + * Purpose: Cannot read source file during file copy. + * %1 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot read file: <filespec>" + */ +#define pszFILERR_READ_FILE "Cannot read file: %1" + + + /*LOCALIZE + * + * Purpose: Cannot write destination file during file copy. + * %1 = filespec + * Generate: DO NOT TRY TO REPRO (code path not used in EXTRACT.EXE) + * Expect: "Cannot write file: <filespec>" + */ +#define pszFILERR_WRITE_FILE "Cannot write file: %1" + +//*** THE END - fileutil.msg diff --git a/private/windows/diamond/filever.c b/private/windows/diamond/filever.c new file mode 100644 index 000000000..903a15fa0 --- /dev/null +++ b/private/windows/diamond/filever.c @@ -0,0 +1,217 @@ +/*** filever.c - Query file version information (Win32-only) + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 07-Jun-1994 bens Split off from fileutil.c + * 05-Aug-1994 bens Don't complain about GetFileVersionInfoSize failing + */ + +#ifndef BIT16 + +//** Get minimal Win32 definitions +//#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ERROR // Override stupid "#define ERROR 0" in wingdi.h + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "filever.h" +#include "filever.msg" + +#include "ttfver.h" + + +BOOL getVer(void *pBlock, + char *pszKey, + void **ppData, + int *pcbData, + char *pszFile, + PERROR perr); + + +/*** getFileVerAndLang - Use VER.DLL API to get file version and language + * + * NOTE: See fileutil.h for entry/exit conditions. + */ +BOOL getFileVerAndLang(char *pszFile, + ULONG *pverMS, + ULONG *pverLS, + char **ppszVersion, + char **ppszLang, + PERROR perr) +{ + char ach[256]; + char achName[256]; + int cb; + DWORD cbFFI; + DWORD cbFVI; + DWORD cbLang; + DWORD dwLangPrimary; + DWORD dwTTFVersion; + DWORD handle; + VS_FIXEDFILEINFO *pFFI; + char *pbFVI; + char *psz; + DWORD *pdwLang; + int rc; + + //** Get size of file version info + cbFVI = GetFileVersionInfoSize(pszFile, &handle); + if (cbFVI == 0) { + //** Doesn't have file version info, try for TrueType version info + if (FGetTTFVersion(pszFile, &dwTTFVersion)) { + *pverMS = dwTTFVersion; + *pverLS = 0; //** What ACME setup wants + } + return TRUE; + } + + //** Allow common error exit point + *ppszVersion = NULL; + *ppszLang = NULL; + + //** Allocate buffer for info + if (!(pbFVI = MemAlloc(cbFVI))) { + ErrSet(perr,pszFILERR_OOM_VER_BUF,"%s",pszFile); + goto error; + } + + //** Get the info + if (!GetFileVersionInfo(pszFile,handle,cbFVI,pbFVI)) { + rc = GetLastError(); + ErrSet(perr,pszFILERR_GFVI_FAILED,"%d%s",rc,pszFile); + goto error; + } + + //** Return version numbers + if (getVer(pbFVI,"\\",&pFFI,&cbFFI,pszFile,perr)) { + *pverMS = pFFI->dwFileVersionMS; + *pverLS = pFFI->dwFileVersionLS; + } + + //** Get language info + if (getVer(pbFVI,"\\VarFileInfo\\Translation",&pdwLang,&cbLang,pszFile,perr)) { + dwLangPrimary = *pdwLang; + //** Format first language codes + sprintf(ach,"%d",LOWORD(dwLangPrimary)); + + //** Format any additional language codes + for (pdwLang++; + cbLang > sizeof(DWORD); + cbLang -= sizeof(DWORD), pdwLang++) { + sprintf(ach+sizeof(ach)," %d",LOWORD(*pdwLang)); + } + + //** Make copy to return to caller + if (!(*ppszLang = MemStrDup(ach))) { + ErrSet(perr,pszFILERR_OOM_DUP_LANG,"%s",pszFile); + goto error; + } + + //** Get version *string* +//BUGBUG 25-May-1994 bens Win32 SDK is unclear about which halfs of the dword +// the Lang and CharSet occupy, so try both! + //** HI/LO is the defacto order used by Windows 3.x files + sprintf(achName,"\\StringFileInfo\\%04x%04x\\FileVersion", + HIWORD(dwLangPrimary), + LOWORD(dwLangPrimary)); + if (!getVer(pbFVI,achName,&psz,&cb,pszFile,perr)) { + //** Try alternate order! + sprintf(achName,"\\StringFileInfo\\%04x%04x\\FileVersion", + LOWORD(dwLangPrimary), + HIWORD(dwLangPrimary)); + getVer(pbFVI,achName,&psz,&cb,pszFile,perr); + } + + //** Duplicate and return string, if we got one + if (psz) { + if (!(*ppszVersion = MemStrDup(psz))) { + ErrSet(perr,pszFILERR_OOM_DUP_VER,"%s",pszFile); + goto error; + } + } + } + + //** Free buffer + MemFree(pbFVI); + + //** Success + return TRUE; + + //** Failure +error: + //** NOTE: Don't free *ppszVersion and *ppszLang in case caller wants + // to use them. + if (pbFVI) { + MemFree(pbFVI); + } + return FALSE; +} /* getFileVerAndLang() */ + + +/*** getVer - Get particular piece of EXE version information + * + * Entry: + * pBlock - Block filled in by GetFileVersionInfo + * pszKey - String to pass to VerQueryValue + * ppData - Pointer to variable to receive pointer to requested + * data insided pBlock. + * pcbData - Pointer to variable to receive length of requested + * data. + * pszFile - File being examined (for error messages) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; *ppData and *pcbData filled in. + * + * Exit-Failure: + * Returns FALSE; Could not get requested data + */ +BOOL getVer(void *pBlock, + char *pszKey, + void **ppData, + int *pcbData, + char *pszFile, + PERROR perr) +{ + int rc; + + if (!VerQueryValue(pBlock,pszKey,ppData,pcbData)) { + rc = GetLastError(); + switch (rc) { + + case NO_ERROR: + case ERROR_RESOURCE_DATA_NOT_FOUND: + case ERROR_RESOURCE_TYPE_NOT_FOUND: + //** Skip the error message + break; + + default: + ErrSet(perr,pszFILERR_VER_QUERY_VALUE,"%d%s%s",rc,pszKey,pszFile); + } + *ppData = NULL; + return FALSE; + } + + //** See if version info was there + if (*pcbData == 0) { + ErrSet(perr,pszFILERR_VER_KEY_MISSING,"%s%s",pszKey,pszFile); + *ppData = NULL; + return FALSE; + } + + //** Success + return TRUE; +} /* getVer() */ + +#endif // !BIT16 diff --git a/private/windows/diamond/filever.h b/private/windows/diamond/filever.h new file mode 100644 index 000000000..71524a8a9 --- /dev/null +++ b/private/windows/diamond/filever.h @@ -0,0 +1,61 @@ +/*** filever.h - Query file version information + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 07-Jun-1994 bens Initial version (code from fileutil.c) + * + * Exported Functions: + * getFileVerAndLang - Use VER.DLL API to get file version and language + */ + +#ifndef INCLUDED_FILEVER +#define INCLUDED_FILEVER 1 + +#include "error.h" +#include <stdio.h> + +#ifndef BIT16 +/*** getFileVerAndLang - Use VER.DLL API to get file version and language + * + * Entry: + * pszFile - Filespec + * pverMS - Receives high (most significant) 32-bit of file version + * pverLS - Receives low (least significant) 32-bit of file version + * ppszVersion - Receives the *string* version resource for the *first* + * language in the file (there could be several). + * NOTE: This string is *not* necessarily the same as a + * sprintf'd version of *pverMS/LS, since build + * procedures are often sloppy!). + * ppszLang - Receives language code formatted as a decimal number. + * If more than language code exists, it is + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, *pverMS, *pverLS, *ppszVersion, and *ppszLang filled in. + * NOTE: It is the *caller's* responsibility to MemFree the strings + * returned in *ppszVersion and *ppszLang (assuming they are not + * NULL)! + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * NOTE: *ppszVersion and *ppszLang may have been returned, even though + * the function failed (the function returns what it can, so that + * the caller can decide whether to ignore the error or not). + * The caller *must* check these values and free them if they are + * *not* NULL! + */ +BOOL getFileVerAndLang(char *pszFile, + ULONG *pverMS, + ULONG *pverLS, + char **ppszVersion, + char **ppszLang, + PERROR perr); +#endif // !BIT16 + +#endif // !INCLUDED_FILEVER diff --git a/private/windows/diamond/filever.msg b/private/windows/diamond/filever.msg new file mode 100644 index 000000000..7ba8dd3d4 --- /dev/null +++ b/private/windows/diamond/filever.msg @@ -0,0 +1,19 @@ +/*** filever.msg - filever.c displayable strings + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 07-Jun-1994 bens Initial version (moved from fileutil.msg) + * 05-Aug-1994 bens Don't complain about GetFileVersionInfoSize failing + */ + +//** Error messages + +#define pszFILERR_OOM_VER_BUF "Out of memory allocating version buffer for file %1" +#define pszFILERR_GFVI_FAILED "GetFileVersionInfo returned %1 on file %2" +#define pszFILERR_OOM_DUP_LANG "Out of memory duplicating language string for file %1" +#define pszFILERR_OOM_DUP_VER "Out of memory duplicating version string for file %1" +#define pszFILERR_VER_QUERY_VALUE "VerQueryValue on key %2 returned %1 on file %3" +#define pszFILERR_VER_KEY_MISSING "No value for key %1 on file %2" diff --git a/private/windows/diamond/fixchg.c b/private/windows/diamond/fixchg.c new file mode 100644 index 000000000..61883b77b --- /dev/null +++ b/private/windows/diamond/fixchg.c @@ -0,0 +1,199 @@ +/* fixchg.c */ + +/* Inoperative changelines frequenly cause problems when switching between */ +/* 1.44Mb diskettes and 1.68Mb DMF diskettes. FixChangeline() tries to */ +/* assure that drives A: and B: will not depend upon proper operation of */ +/* the drive's changeline. If these efforts fail, it's no big deal; we */ +/* do this without even knowing whether the changeline works or not. */ + +#include "fixchg.h" /* prototype verification */ + +/* --- definitions -------------------------------------------------------- */ + +/* See Microsoft MS-DOS Programmer's Reference V6.0, p.38, 312, 319 */ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; + +#pragma pack (1) + +typedef struct +{ + WORD tklSectorNum; /* this physical position's sector number */ + WORD tklSectorSize; /* size of this sector in bytes */ +} NUMSIZE; + +typedef struct +{ + WORD tklSectors; /* number of sectors in the layout */ + NUMSIZE tklNumSize[1]; /* don't need much of this, not used here */ +} TRACKLAYOUT; + +typedef struct +{ + WORD dpBytesPerSec; /* bytes per sector */ + BYTE dpSecPerClust; /* sectors per cluster */ + WORD dpResSectors; /* reserved sectors */ + BYTE dpFATs; /* number of copies of the FAT */ + WORD dpRootDirEnts; /* number of entries in the root directory */ + WORD dpSectors; /* total # of sectors, 0->more than 64k */ + BYTE dpMedia; /* media descriptor byte */ + WORD dpFATsecs; /* sectors per copy of the FAT */ + WORD dpSecPerTrack; /* sectors per track */ + WORD dpHeads; /* number of heads */ + DWORD dpHiddenSecs; /* sectors hidden before boot sector */ + DWORD dpHugeSectors; /* number of sectors if > 64k sectors */ + WORD reserved[3]; +} BPB; + +typedef struct +{ + BYTE dpSpecFunc; /* special functions */ + BYTE dpDevType; /* device type, 7=1.44Mb, 9=2.88Mb, etc. */ + WORD dpDevAttr; /* device's attributes */ + WORD dpCylinders; /* number of cylinders */ + BYTE dpMediaType; /* media type, more like density code */ + BPB dpBPB; /* the BPB (default or current) */ + TRACKLAYOUT dpTrackLayout; /* track layout field appended for set call */ +} DEVICEPARAMS, far *PFDEVICEPARAMS; + +#pragma pack() + +#define SPECIAL_GET_DEFAULT 0 /* get information for default media */ +#define SPECIAL_SET_DEFAULT 4 /* set default media, good track layout */ + +#define ATTR_NONREMOVABLE 1 /* attr bit for non-removable device */ +#define ATTR_CHANGELINE 2 /* attr bit for changeline supported */ + +/* --- FixChangelines() --------------------------------------------------- */ + +#pragma warning(disable:4704) /* no in-line balking */ + +void FixChangelines(void) +{ + WORD dosVersion; + DEVICEPARAMS dp; + PFDEVICEPARAMS pfDp; + WORD drive; + WORD owner; + + _asm mov ah,30h ; get DOS version + _asm int 21h + _asm xchg ah,al + _asm mov dosVersion,ax + + + /* these IoCtls were new to MS-DOS 3.2. (But then, 1.44Mb drives */ + /* weren't supported until 3.3, so needing this is pretty unlikely.) */ + + if (dosVersion < (0x300 + 20)) + { + return; /* prior versions don't need help */ + } + + pfDp = &dp; /* make a far pointer to DEVICEPARAMS structure */ + + for (drive = 1; drive <= 2; drive++) /* do A: and B: */ + { + /* get drive owner so we can restore it */ + + _asm mov owner,0 ; assume not shared + _asm mov ax,440Eh ; Get Logical Drive Map + _asm mov bx,drive ; drive number + _asm int 21h ; execute DOS request + _asm jc no_owner ; if failed + _asm mov owner,ax ; save owner (AL) + + /* set drive owner to suppress "Insert diskette for drive..." */ + + _asm mov ax,440Fh ; Set Logical Drive Map + _asm mov bx,drive ; drive number + _asm int 21h ; execute DOS request + + /* MS-DOS 5.0 added query Ioctl, to see if the calls we need are */ + /* supported. This is highly unlikely to fail. */ + +no_owner: + + if (dosVersion >= 0x500) + { + _asm mov ax,4411h ; Query Ioctl device + _asm mov bx,drive ; drive number + _asm mov cx,0840h ; check on SET DEVICE PARAMETERS + _asm int 21h ; execute DOS request + _asm jc failed ; if not supported + + _asm mov ax,4411h ; Query Ioctl device + _asm mov bx,drive ; drive number + _asm mov cx,0860h ; check on GET DEVICE PARAMETERS + _asm int 21h ; execute DOS request + _asm jc failed ; if not supported + } + + + /* get information about this physical device */ + + dp.dpSpecFunc = SPECIAL_GET_DEFAULT; + + _asm push ds ; preserve data selector + _asm mov ax,440Dh ; generic IoCtl + _asm mov bx,drive ; drive number 1=A: 2=B: + _asm mov cx,0860h ; DISK / GET DEVICE PARAMETERS + _asm lds dx,pfDp ; pointer to DEVICEPARAMS structure + _asm int 21h ; execute DOS request + _asm pop ds ; restore data selector + _asm jc failed ; if error + + + /* is this device is removable and claims changeline is supported? */ + + if ((dp.dpDevAttr & (ATTR_NONREMOVABLE | ATTR_CHANGELINE)) == + ATTR_CHANGELINE) /* if removable with changeline: */ + { + /* modify device to "changeline not supported" */ + + dp.dpSpecFunc = SPECIAL_SET_DEFAULT; + dp.dpDevAttr &= ~ATTR_CHANGELINE; /* disable changeline */ + dp.dpTrackLayout.tklSectors = 0; /* no layout being sent */ + dp.dpBPB.reserved[0] = 0; + dp.dpBPB.reserved[1] = 0; + dp.dpBPB.reserved[2] = 0; + + _asm push ds ; preserve data selector + _asm mov ax,440Dh ; generic IoCtl + _asm mov bx,drive ; drive number 1=A: 2=B: + _asm mov cx,0840h ; DISK / SET DEVICE PARAMETERS + _asm lds dx,pfDp ; pointer to DEVICEPARAMS structure + _asm int 21h ; execute DOS request + _asm pop ds ; restore data selector + } + +failed: + /* restore initial drive owner */ + + _asm mov ax,440Fh ; Set Logical Drive Map + _asm mov bx,owner ; drive number + _asm or bx,bx ; is it shared? + _asm jz nextdrive ; if not shared + _asm int 21h ; execute DOS request + +nextdrive: + continue; /* C labels require some statement */ + } + + return; +} + +/* --- stand-alone test stub ---------------------------------------------- */ + +#ifdef STANDALONE + +void main(void) +{ + FixChangelines(); +} + +#endif + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/fixchg.h b/private/windows/diamond/fixchg.h new file mode 100644 index 000000000..473a95a33 --- /dev/null +++ b/private/windows/diamond/fixchg.h @@ -0,0 +1,11 @@ +/* fixchg.h */ + +/* Inoperative changelines usually cause problems when switching between */ +/* 1.44Mb diskettes and 1.68Mb DMF diskettes. FixChangeline() tries to */ +/* assure that drives A: and B: will not depend upon proper operation of */ +/* the drive's changeline. */ + +#ifndef INCLUDED_FIXCHG +#define INCLUDED_FIXCHG +extern void FixChangelines(void); +#endif //INCLUDED_FIXCHG diff --git a/private/windows/diamond/format.c b/private/windows/diamond/format.c new file mode 100644 index 000000000..4037f76ee --- /dev/null +++ b/private/windows/diamond/format.c @@ -0,0 +1,185 @@ +/*** format.c - Parameter Formatter + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 28-Apr-1994 bens Initial version + * 10-May-1994 bens Add braces support + */ + +#include <memory.h> +#include <string.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "message.h" +#include "misc.h" + +#include "inf.h" +#include "format.h" +#include "format.msg" + + +/*** SubstituteFormatString - Do parameter substitution + * + * NOTE: See format.h for entry/exit conditions. + */ +BOOL SubstituteFormatString(char *pszDst, + int cbDst, + char *pszSrc, + PFNFORMATPARM pfnfp, + void *pv, + PERROR perr) +{ + char achParmName[cbPARM_NAME_MAX]; + int cb; + int cch; + BOOL fParmSeen; // TRUE => parm seen in {braces} + BOOL fParmEmpty; // TRUE => all parms were empty in {opt} + char *pch; + char *pszAfterParm; // Points to first char after var subst. + char *pszOptStart; // Start of optional text + char *pszParmStart; // Points to first * in var substitution + + //** Not in conditional test + pszOptStart = NULL; + + //** Process string for parameters and conditional text + while (*pszSrc != '\0') { + switch (*pszSrc) { + + case chFP_LBRACE: + pszSrc++; // Eat the left brace + if (*pszSrc == chFP_LBRACE) { // Have "{{" + //** Collapse two {{ into one { + if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) { + goto error_copying; + } + } + else { + //** Make sure we're not already in a brace section + if (pszOptStart) { + ErrSet(perr,pszFMTERR_NESTED_BRACES,"%c%s", + chFP_LBRACE,pszSrc); + return FALSE; + } + pszOptStart = pszDst; // Save start of conditional text + fParmSeen = FALSE; // No parm seen, yet + fParmEmpty = TRUE; // Assume parm was empty + } + break; + + case chFP_RBRACE: + pszSrc++; // Eat the right brace + if (*pszSrc == chFP_RBRACE) { // Have "}}" + //** Collapse two }} into one } + if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) { + goto error_copying; + } + } + else { + //** Make sure we are in a brace section + if (!pszOptStart) { + ErrSet(perr,pszFMTERR_RIGHT_WITH_NO_LEFT,"%c%c%s", + chFP_RBRACE,chFP_LBRACE,pszSrc); + return FALSE; + } + //** Omit text if we need to + if (fParmSeen && fParmEmpty) { // All parms were empty + //** Remove optional text + Assert(pszDst >= pszOptStart); + cbDst += pszDst - pszOptStart; + pszDst = pszOptStart; + } + //** Reset state variables + pszOptStart = NULL; + } + break; + + case chFP_MARKER: + pszParmStart = pszSrc; // Save start for error messgages + pszSrc++; // Skip first * + if (*pszSrc == chFP_MARKER) { // Have "**" + //** Collapse two ** into one * + if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) { + goto error_copying; + } + } + else { + //** Attempt parameter substitution + pch = strchr(pszSrc,chFP_MARKER); // Finding ending * + if (!pch) { // No terminating * + ErrSet(perr,pszFMTERR_MISSING_SUBST,"%c%s", + chFP_MARKER,pszParmStart); + return FALSE; + } + pszAfterParm = pch+1; // Point after ending * + + //** Extract parameter name + cch = pch - pszSrc; // Length of parameter name + if (cch >= sizeof(achParmName)) { + ErrSet(perr,pszFMTERR_PARM_NAME_TOO_LONG,"%d%s", + sizeof(achParmName)-1,pszParmStart); + return FALSE; + } + memcpy(achParmName,pszSrc,cch); // Copy it + achParmName[cch] = '\0'; // Terminate it + + //** Get parameter value + if (-1 == (cb = (*pfnfp)(pszDst, // Destination + cbDst, // Space remaining + achParmName, // Parm name + pv, // Context + perr))) { + return FALSE; // Error already filled in + } + //** Adjust output buffer and space remaining + // NOTE: Don't count NUL byte, as we do that at the very end + Assert(cbDst >= cb); + pszDst += cb; + cbDst -= cb; + + //** Remember we saw a parm, and if it was not empty + fParmSeen = TRUE; + if (cb > 0) { + fParmEmpty = FALSE; // At least one parm not empty + } + + //** Skip over parameter name + pszSrc = pszAfterParm; + } + break; + + default: + //** Just copy the character + if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) { + goto error_copying; + } + } + } /* while */ + + //** Make sure we didn't leave an open optional text section + if (pszOptStart) { + ErrSet(perr,pszFMTERR_MISSING_RIGHT_BRACE,"%c",chFP_RBRACE); + return FALSE; + } + + //** Terminate processed string + if (cbDst == 0) { // No room for terminator + goto error_copying; + } + *pszDst++ = '\0'; // Terminate string + + //** Success + return TRUE; + +error_copying: + ErrSet(perr,pszFMTERR_COPYING_OVERFLOW,"%s",pszSrc); + return FALSE; +} /* SubstituteFormatString() */ diff --git a/private/windows/diamond/format.h b/private/windows/diamond/format.h new file mode 100644 index 000000000..324253d5c --- /dev/null +++ b/private/windows/diamond/format.h @@ -0,0 +1,87 @@ +/*** format.h - Definitions for Parameter Formatter + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 05-Apr-1994 bens Initial version + * 28-Apr-1994 bens Implemented + * 10-May-1994 bens Add braces support + */ + +#ifndef INCLUDED_FORMAT +#define INCLUDED_FORMAT 1 + +#include "types.h" +#include "asrt.h" +#include "error.h" + + +/*** PFNFORMATPARM - Function to format a parameter + * + * This functions takes a parameter name and a context and returns + * the formatted value of that parameter. + * + * Entry: + * pszOut - Output buffer + * cbOut - Size of output buffer + * pszParm - Parameter name to generate value for + * pv - Context for formatting function + * perr - ERROR structure + * + * Exit-Success: + * Returns length of generated parameter, which is stored in pszOut. + * (Length can be zero). + * + * Exit-Failure: + * Returns -1; perr filled in. + */ +typedef int (*PFNFORMATPARM)(char *pszOut, + int cbOut, + char *pszParm, + void *pv, + PERROR perr); /* pfnfp */ +#define FNFORMATPARM(fn) int fn(char *pszOut, \ + int cbOut, \ + char *pszParm, \ + void *pv, \ + PERROR perr) + + +/*** SubstituteFormatString - Do parameter substitution + * + * Entry: + * pszDst - Output buffer + * cbDst - Size of output buffer + * pszSrc - Source line to perform parameter substition upon + * pfnfp - Parameter formatting function + * pv - Parameter passed to formatting functions + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; pszLine filled in + * + * Exit-Failure: + * Returns FALSE; perr filled in with details. + * + * Substitution rules: + * (1) Only one level of substitution is performed + * (2) Parameter values are obtained from pfnfp + * (3) "**" is replaced by "*", if the first "*" is not the end of + * of a variable substitution. + * (4) Text bracketed by braces "{}" is only generated if a parameter + * inside the braces has a non-empty value. + * (5) "{{" is replaced by "{", and "}}" is replaced by "}" -- no + * conditional text generation (see (4)) is performed. + */ +BOOL SubstituteFormatString(char *pszDst, + int cbDst, + char *pszSrc, + PFNFORMATPARM pfnfp, + void *pv, + PERROR perr); +#endif // INCLUDED_FORMAT diff --git a/private/windows/diamond/format.msg b/private/windows/diamond/format.msg new file mode 100644 index 000000000..f924d0f30 --- /dev/null +++ b/private/windows/diamond/format.msg @@ -0,0 +1,32 @@ +/*** format.msg - Displayable strings for format.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 28-Apr-1994 bens Initial version + * 10-May-1994 bens Add braces support + */ + + +//** Special characters + +#define chFP_MARKER '*' // Character to surround parameters with +#define chFP_LBRACE '{' // Left Brace for conditional text +#define chFP_RBRACE '}' // Right Brace for conditional text + + +//** Error Messages + +#define pszFMTERR_MISSING_SUBST "Missing %1 after parameter name: %2" +#define pszFMTERR_PARM_NAME_TOO_LONG "Parameter name exceeds maximum length(%1): %2" +#define pszFMTERR_COPYING_OVERFLOW "Buffer overflow while copying: %1" + + +#define pszFMTERR_NESTED_BRACES "Nested braces (%1) not allowed: %2" +#define pszFMTERR_RIGHT_WITH_NO_LEFT "Right brace (%1) with no left brace (%2): %3" +#define pszFMTERR_MISSING_RIGHT_BRACE "Missing right brace (%1)" diff --git a/private/windows/diamond/funlist.h b/private/windows/diamond/funlist.h new file mode 100644 index 000000000..9a92d9ac2 --- /dev/null +++ b/private/windows/diamond/funlist.h @@ -0,0 +1,141 @@ +/*** funlist.h - Definitions for Function List Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 05-Apr-1994 bens Initial version + * + * Exported Functions: + */ + +#ifndef INCLUDED_FUNLIST +#define INCLUDED_FUNLIST 1 + +#include "types.h" +#include "asrt.h" +#include "error.h" + + +typedef void * HFUNLIST; /* hfunlist - list of functions */ +typedef void * HFUNCTION; /* hfun - FUNCTION handle */ + + +/*** PFNFUNCTION - function pointer for function list + * + * This is just a generic function declaraion. The client that uses the + * function list will cast the function pointer to the correct type before + * actually calling the function. + */ +typedef void (*PFNFUNCTION)(void); /* pfnfun */ + + +/*** FunAdd - Add a function to a function list + * + * Entry: + * hfunlist - Function list to check + * pszKey - Key value for function lookup + * pfnfun - Function pointer + * perr - ERROR structure + * + * Exit-Success: + * Returns hfun, function is added to list + * + * Exit-Failure: + * Returns NULL, cannot add function to list + * ERROR structure filled in with details of error. + */ +HFUNCTION FunAdd(HFUNLIST hfunlist, + char *pszKey, + PFNFUNCTION pfnfun, + PERROR perr); + + +/*** FunCreateList - Create a list of functions + * + * Entry: + * pfnfun - Function pointer for *default* function (NULL if not supplied) + * perr - ERROR structure + * + * Exit-Success: + * Returns HFUNLIST; list is created. + * + * Exit-Failure: + * Returns NULL, cannot create list; perror filled in with error. + */ +HFUNLIST FunCreateList(PERROR perr); + + +/*** FunDestroyList - Destroy a list of functions + * + * Entry: + * hfunlist - function list to destroy + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; list was destroyed. + * + * Exit-Failure: + * Returns FALSE, cannot destroy list; perror filled in with error. + */ +BOOL FunDestroyList(HFUNLIST hfunlist, PERROR perr); + + +/*** FunFind - See if function exists + * + * Entry: + * hfunlist - Function list + * pszKey - Function key to look for (case-insensitive search) + * Pass NULL to find the default function. + * perr - ERROR structure + * + * Exit-Success: + * Returns hfun, if function exists. If key was not found, but a default + * function was supplied on the FunCreateList() call, then that default + * function is returned. + * + * Exit-Failure: + * Returns NULL, function does not exist (key not found and no default + * function was supplied on the FunCreateList() call). + * ERROR structure filled in with details of error. + */ +HFUNCTION FunFind(HFUNLIST hfunlist, + char *pszKey, + PERROR perr); + + +/*** FunRemove - Remove function from a function list + * + * Entry: + * hfun - function handle + * + * Exit-Success: + * Always works, since hfun is assumed to be valid. + */ +void FunRemove(HFUNCTION hfun); + + +/*** FunGetFunction - Get the function pointer for a particular key value + * + * Entry: + * hfunlist - Function list + * pszKey - Function key to look for (case-insensitive search) + * perr - ERROR structure + * + * Exit-Success: + * Returns hfun, if function exists. If key was not found, but a default + * function was supplied on the FunCreateList() call, then that default + * function is returned. + * + * Exit-Failure: + * Returns NULL, function does not exist (key not found and no default + * function was supplied on the FunCreateList() call). + * If an error occured, perr is filled in. + */ +PFNFUNCTION FunGetFunction(HFUNLIST hfunlist, char *pszKey); + +#endif // INCLUDED_FUNLIST diff --git a/private/windows/diamond/glist.c b/private/windows/diamond/glist.c new file mode 100644 index 000000000..fc9cfb359 --- /dev/null +++ b/private/windows/diamond/glist.c @@ -0,0 +1,777 @@ +/*** glist.c - Generic List Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 06-Apr-1994 bens Initial version + * 22-Apr-1994 bens Add GLFindNext, GLSetValue, GLGetValue + * 26-Apr-1994 bens Add GLFirstItem, GLNextItem, GLPreviousItem + * 18-May-1994 bens Add hashing for quick lookup + * + * Notes: + * (1) To speed up name searches on large lists, we create a hash table + * the first time a name search -- GLFind() or GLFindAndGetValue() + * -- is performed on a a list with at least 50 items. From then on, + * the hash table is updated as new items are added or removed. + */ + +#include <string.h> +#include <stdlib.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" +#include "glist.h" + +#include "glist.msg" + +/* + ** GENERIC item definition + */ + +//** Empty definition, to avoid "chicken and the egg" problem +typedef struct GENLIST_t GENLIST; /* gen */ +typedef GENLIST *PGENLIST; /* glist */ + + +typedef struct GENERIC_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigGENERIC +#endif + char *pszKey; // key + void *pv; // current value + struct GENERIC_t *pgenHash; // next item in hash chain + struct GENERIC_t *pgenNext; // next item in full list + struct GENERIC_t *pgenPrev; // previous item in full list + PGENLIST pglist; // List containing this item +} GENERIC; /* gen */ +typedef GENERIC *PGENERIC; /* pgen */ + +#ifdef ASSERT +#define sigGENERIC MAKESIG('G','I','T','M') // GENERIC signature +#define AssertGen(pgen) AssertStructure(pgen,sigGENERIC); +#else // !ASSERT +#define AssertGen(pgen) +#endif // !ASSERT + + +/* + ** HASHTABLE definition + * + * See hashString() function for use of the following constants. + */ + +#define cHASH_TOP_BITS 3 // Hash index bits that get rotated +#define cHASH_BOT_BITS 8 // Hash index bits that get shifted left +#define cHASH_BITS (cHASH_TOP_BITS+cHASH_BOT_BITS) // Total bits in hash +#define cHASH_ENTRIES (1<<cHASH_BITS) // Total number of hash entries +#define HASH_MASK (cHASH_ENTRIES-1) // Mask for hash index +#define HASH_BOT_MASK ((1 << cHASH_BOT_BITS)-1) // Mask for bottom bits + +#define cHASH_THRESHOLD 50 // Number of elements in a generic list + // that will trigger hashing + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigGENLIST +#endif + PGENERIC apgen[cHASH_ENTRIES]; // Hash table +} HASHTABLE; /* ht */ +typedef HASHTABLE *PHASHTABLE; /* pht */ +#ifdef ASSERT +#define sigHASHTABLE MAKESIG('H','A','S','H') // HASHTABLE signature +#define AssertHT(pv) AssertStructure(pv,sigHASHTABLE); +#else // !ASSERT +#define AssertHT(pv) +#endif // !ASSERT + + +/* + ** GENLIST definition + */ + +typedef struct GENLIST_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigGENLIST +#endif + PGENERIC pgenHead; // First item in list + PGENERIC pgenTail; // Last item in list + void *pvDefault; // Default value + PFNGLDESTROYVALUE pfngldv; // Value destroying function + int cItems; // Count of items on list + PHASHTABLE pht; // Optional hash table +} GENLIST; /* glist */ +//typedef GENLIST *PGENLIST; /* pglist */ +#ifdef ASSERT +#define sigGENLIST MAKESIG('G','L','S','T') // GENLIST signature +#define AssertGList(pv) AssertStructure(pv,sigGENLIST); +#else // !ASSERT +#define AssertGList(pv) +#endif // !ASSERT + + +#define HGENfromPGEN(h) ((PGENERIC)(h)) +#define PGENfromHGEN(p) ((HGENERIC)(p)) + +#define HGLfromPGL(h) ((PGENLIST)(h)) +#define PGLfromHGL(p) ((HGENLIST)(p)) + + +//** Function Prototypes + +PGENERIC findItem(PGENERIC pgen, char *pszKey); +int hashString(char *psz); +void hashItem(PGENLIST pglist, PGENERIC pgen); + + +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// +// Exported Functions +// +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +/*** GLAdd - Add an item to a list + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENERIC GLAdd(HGENLIST hglist, + char *pszKey, + void *pv, + char *pszDesc, + BOOL fUnique, + PERROR perr) +{ + PGENERIC pgen; + PGENLIST pglist; + + pglist = PGLfromHGL(hglist); + AssertGList(pglist); + + //** If requested, make sure item does not already exist + if (fUnique && (pgen = findItem(pglist->pgenHead,pszKey))) { + ErrSet(perr,pszGLERR_ALREADY_CREATED,"%s%s",pszDesc,pszKey); + perr->code = ERRGLIST_NOT_UNIQUE; //** Indicate cause + perr->pv = HGENfromPGEN(pgen); //** Return existing item + return NULL; + } + + //** Create item + if (!(pgen = MemAlloc(sizeof(GENERIC)))) { + goto error; + } + + SetAssertSignature(pgen,sigGENERIC); + pgen->pszKey = NULL; // Make sure not to free garbage! + pgen->pv = NULL; // Make sure not to free garbage! + + //** Make copy of name + if (pszKey) { + if (!(pgen->pszKey = MemStrDup(pszKey))) { + goto error; + } + } + else { + pszKey = NULL; + } + + //** Set value + pgen->pv = pv; + + //** Link item into list + pgen->pgenHash = NULL; // Always last on hash list + pgen->pgenNext = NULL; // Always last on list + pgen->pgenPrev = pglist->pgenTail; // Always points to last item on list + + if (pglist->pgenHead == NULL) { + pglist->pgenHead = pgen; + pglist->pgenTail = pgen; + } + else { + AssertGen(pglist->pgenTail); + pglist->pgenTail->pgenNext = pgen; // Add to end of list + pglist->pgenTail = pgen; // New tail + } + + //** Remember which list we are on! + pgen->pglist = pglist; + + //** Update count of item in list + pglist->cItems++; + Assert(pglist->cItems > 0); + + //** Add to hash table, if we are hashing + if (pglist->pht != NULL) { + hashItem(pglist,pgen); + } + + //** Success + return HGENfromPGEN(pgen); + +error: + if (!pgen) { + if (!(pgen->pszKey)) { + MemFree(pgen->pszKey); + } + MemFree(pgen); + } + if (!ErrIsError(perr)) { + ErrSet(perr,pszGLERR_OUT_OF_MEMORY_ITEM,"%s%s",pszDesc,pszKey); + } + return NULL; +} /* GLAdd() */ + + +/*** GLDelete - Delete item from a list + * + * NOTE: See glist.h for entry/exit conditions. + */ +void GLDelete(HGENERIC hgen) +{ + int i; + PGENERIC pgen; + PGENERIC pgenPred; + PGENLIST pglist; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + + pglist = pgen->pglist; + AssertGList(pglist); + AssertGen(pglist->pgenHead); + AssertGen(pglist->pgenTail); + + //** Free value, if function available + if (pglist->pfngldv != NULL) { + (*pglist->pfngldv)(pgen->pv); // Destroy value + } + + //** Free memory for item name + if (pgen->pszKey) { + MemFree(pgen->pszKey); + } + + //** Adjust forward list pointers + if (pgen->pgenPrev == NULL) { // At head of list + pglist->pgenHead = pgen->pgenNext; // Remove from forward chain + } + else { // At middle or end of list + AssertGen(pgen->pgenPrev); + pgen->pgenPrev->pgenNext = pgen->pgenNext; // Remove from forward chain + } + + //** Adjust backward list pointers + if (pgen->pgenNext == NULL) { // At tail of list + pglist->pgenTail = pgen->pgenPrev; // Remove from backward chain + } + else { + AssertGen(pgen->pgenNext); + pgen->pgenNext->pgenPrev = pgen->pgenPrev; // Remove from backward chain + } + + //** Adjust hash links, if present + if (pglist->pht) { + Assert(pgen->pszKey != NULL); // No null keys for hash tables + i = hashString(pgen->pszKey); + AssertHT(pglist->pht); + if (pgen == pglist->pht->apgen[i]) { // First item on hash list + pglist->pht->apgen[i] = pgen->pgenHash; // Update head of list + } + else { + //** Search for predecessor + for (pgenPred=pglist->pht->apgen[i]; + pgenPred && (pgenPred->pgenHash != pgen); + pgenPred=pgenPred->pgenHash) { + ; //** Keep scanning until end of list or predecessor + } + + //** Remove item from hash list + if (pgenPred) { + pgenPred->pgenHash = pgen->pgenHash; + } + else { + Assert(0); // Oops, we weren't on the list! + } + } + } + + //** Item is disentagled from all lists + ClearAssertSignature(pgen); + MemFree(pgen); + + //** Update count of item in list + pglist->cItems--; + Assert(pglist->cItems >= 0); +} /* GLDelete() */ + + +/*** GLCreateList - Create a list of + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENLIST GLCreateList(void *pv, + PFNGLDESTROYVALUE pfngldv, + char *pszDesc, + PERROR perr) +{ + PGENLIST pglist; + + if (!(pglist = MemAlloc(sizeof(GENLIST)))) { + ErrSet(perr,pszGLERR_OUT_OF_MEMORY_LIST,"%s",pszDesc); + return NULL; + } + + SetAssertSignature(pglist,sigGENLIST); + pglist->pgenHead = NULL; // Empty list + pglist->pgenTail = NULL; // Empty list + pglist->pvDefault = pv; // Default value + pglist->pfngldv = pfngldv; // Value destroying function + pglist->cItems = 0; // No items on list, yet + pglist->pht = NULL; // No hash table, yet +//BUGBUG 11-Apr-1994 bens Add dummy node, then we can store the default +// value there, and simplify the GLDelete code a little bit. + + return HGLfromPGL(pglist); +} /* GLCreateList() */ + + +/*** GLCopyToList - Copy items from one list to another + * + * NOTE: See glist.h for entry/exit conditions. + */ +BOOL GLCopyToList(HGENLIST *phglistDst, + HGENLIST hglistSrc, + PFNGLDUPLICATEVALUE pfngldup, + char *pszDesc, + PERROR perr) +{ + PGENERIC pgen; + PGENLIST pglistDst; + PGENLIST pglistSrc; + void *pvNew; + + pglistSrc = PGLfromHGL(hglistSrc); + if (!pglistSrc) { // Source list is empty + return TRUE; // Success + } + + AssertGList(pglistSrc); // Make sure source is valid + pglistDst = PGLfromHGL(*phglistDst); + if (pglistDst) { // Destination list exists + AssertGList(pglistDst); // Make sure destination is valid + //** Value types must be compatible + Assert(pglistDst->pfngldv == pglistSrc->pfngldv); + } + + //** Copy items from source to destination list + for (pgen=pglistSrc->pgenHead; pgen; pgen=pgen->pgenNext) { + //** Create destination list if necessary + if (!pglistDst) { // Need to create destination + pglistDst = GLCreateList(pglistSrc->pvDefault, + pglistSrc->pfngldv, + pszDesc, + perr); + if (!pglistDst) { + return FALSE; // Error already filled in + } + *phglistDst = HGLfromPGL(pglistDst); // Return to caller + } + + //** Make sure item not already in destination list + if (!findItem(pglistDst->pgenHead,pgen->pszKey)) { + //** New item for destination list + if (pfngldup != NULL) { + if (!(pvNew = (*pfngldup)(pgen->pv))) { + ErrSet(perr,pszGLERR_OUT_OF_MEMORY_DUP,"%s",pszDesc); + return FALSE; + } + } + else { + pvNew = pgen->pv; // Assume it is a scalar + } + //** Add item to destination list + if (!GLAdd(*phglistDst, + pgen->pszKey, + pvNew, + pszDesc, + FALSE, + perr)) { + if (pglistDst->pfngldv) { + (*pglistDst->pfngldv)(pvNew); // Destroy value + } + return FALSE; + } + } + } + //** Success + return TRUE; +} /* GLCopyToList() */ + + +/*** GLDestroyList - Destroy a list + * + * NOTE: See glist.h for entry/exit conditions. + */ +void GLDestroyList(HGENLIST hglist) +{ + PGENERIC pgen; + PGENERIC pgenNext; + PGENLIST pglist; + + pglist = PGLfromHGL(hglist); + AssertGList(pglist); + + //** Free the hash table, if present + if (pglist->pht) { + AssertHT(pglist->pht); + MemFree(pglist->pht); + ClearAssertSignature(pglist->pht); + + //** Tell GLDelete() not to bother updating the hash chains! + pglist->pht = NULL; + } + + //** Free all of the items + for (pgen=pglist->pgenHead; pgen != NULL; ) { + AssertGen(pgen); + pgenNext = pgen->pgenNext; // Save next pgen before we free pgen! + GLDelete(HGENfromPGEN(pgen)); + pgen = pgenNext; // Use it + } + ClearAssertSignature(pglist); + + //** Free the list itself + MemFree(pglist); +} /* GLDestroyList() */ + + +/*** GLFind - Search for item in list + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENERIC GLFind(HGENLIST hglist, + char *pszKey, + char *pszDesc, + PERROR perr) +{ + PGENLIST pglist; + PGENERIC pgen; + + pglist = PGLfromHGL(hglist); + AssertGList(pglist); + + //** Find item + pgen = findItem(pglist->pgenHead, pszKey); + if (pgen) { + return HGENfromPGEN(pgen); + } + else { + ErrSet(perr,pszGLERR_ITEM_NOT_FOUND,"%s%s",pszDesc,pszKey); + return HGENfromPGEN(NULL); + } +} /* GLFind() */ + + +/*** GLFindNext - Search for next item in list + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENERIC GLFindNext(HGENERIC hgen, + char *pszKey) +{ + PGENERIC pgen; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + + //** Find item, starting *after* passed in item + return findItem(pgen->pgenNext, pszKey); +} /* GLFindNext() */ + + +/*** GLFirstItem - Get first item from a list + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENERIC GLFirstItem(HGENLIST hglist) +{ + PGENLIST pglist; + + pglist = PGLfromHGL(hglist); + AssertGList(pglist); + return HGENfromPGEN(pglist->pgenHead); +} /* GLFirstItem() */ + + +/*** GLNextItem - Get next item + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENERIC GLNextItem(HGENERIC hgen) +{ + PGENERIC pgen; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + return HGENfromPGEN(pgen->pgenNext); +} /* GLNextItem() */ + + +/*** GLPreviousItem - Get previous item + * + * NOTE: See glist.h for entry/exit conditions. + */ +HGENERIC GLPreviousItem(HGENERIC hgen) +{ + PGENERIC pgen; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + return HGENfromPGEN(pgen->pgenPrev); +} /* GLPreviousItem() */ + + +/*** GLGetKey - Get the item key + * + * NOTE: See glist.h for entry/exit conditions. + */ +char *GLGetKey(HGENERIC hgen) +{ + PGENERIC pgen; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + + return pgen->pszKey; +} /* GLGetKey() */ + + +/*** GLGetValue - Get the item value for a particular item + * + * NOTE: See glist.h for entry/exit conditions. + */ +void *GLGetValue(HGENERIC hgen) +{ + PGENERIC pgen; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + + return pgen->pv; +} /* GLGetValue() */ + + +/*** GLSetValue - Set the item value for a particular item + * + * NOTE: See glist.h for entry/exit conditions. + */ +void GLSetValue(HGENERIC hgen, void *pv) +{ + PGENERIC pgen; + + pgen = PGENfromHGEN(hgen); + AssertGen(pgen); + + pgen->pv = pv; +} /* GLSetValue() */ + + +/*** GLFindAndGetValue - Get the item value for a particular item + * + * NOTE: See glist.h for entry/exit conditions. + */ +void *GLFindAndGetValue(HGENLIST hglist, + char *pszKey) +{ + PGENERIC pgen; + PGENLIST pglist; + + pglist = PGLfromHGL(hglist); + if (!pglist) { // No list + return NULL; // Nothing to find + } + AssertGList(pglist); + + //** Find item + pgen = findItem(pglist->pgenHead, pszKey); + if (pgen) { + AssertGen(pgen); + return pgen->pv; + } + else { + return pglist->pvDefault; + } +} /* GLFindAndGetValue() */ + + +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// +// Private Functions +// +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +/*** findItem - See if item exists + * + * Entry: + * pgen - Starting item for search + * pszKey - Name to look for + * + * Exit-Success: + * Returns PGENERIC, if item exists. + * + * Exit-Failure: + * Returns NULL, item does not exist. + * ERROR structure filled in with details of error. + */ +PGENERIC findItem(PGENERIC pgenStart, char *pszKey) +{ + int i; + PGENERIC pgen; + PGENLIST pglist; + + //** Quick out for empty list + if (!pgenStart) { + return NULL; + } + + AssertGen(pgenStart); + pglist = pgenStart->pglist; + AssertGList(pglist); + + //** Use hash table, if present + if (pglist->pht) { + //** pgenStart must be pglist->pgenHead, because the hash search + // is undefined otherwise. + Assert(pgenStart == pglist->pgenHead); + Assert(pszKey != NULL); + i = hashString(pszKey); + AssertHT(pglist->pht); + for (pgen=pglist->pht->apgen[i]; pgen; pgen=pgen->pgenHash) { + if (!_stricmp(pgen->pszKey,pszKey)) { // Got a match! + return HGENfromPGEN(pgen); // Return item handle + } + } + //** Did not find item + return NULL; + } + else if (pglist->cItems >= cHASH_THRESHOLD) { + //** Create a hash table if keys are not NULL + if (pszKey) { + //** Create hash table, so *next* search is faster + if (pglist->pht = MemAlloc(sizeof(HASHTABLE))) { + SetAssertSignature(pglist->pht,sigHASHTABLE); + //** Initialize table + for (i=0; i<cHASH_ENTRIES; i++) { + pglist->pht->apgen[i] = NULL; // Hash list is empty + } + //** Hash in all the items already in the list + for (pgen=pglist->pgenHead; pgen; pgen=pgen->pgenNext) { + hashItem(pglist,pgen); + } + } + else { + //** Out of memory; No need to fail -- we can still do + // a linear search! + } + } + } + + //** Do a linear search + for (pgen=pgenStart; pgen != NULL; pgen = pgen->pgenNext) { + AssertGen(pgen); + if (pszKey && pgen->pszKey) { // Both keys are strings + if (!_stricmp(pgen->pszKey,pszKey)) { // Got a match! + return HGENfromPGEN(pgen); // Return item handle + } + } + else { // At least one key is NULL + if (pszKey == pgen->pszKey) { // Got a match + return HGENfromPGEN(pgen); // Return item handle + } + } + } + + //** Did not find item + return NULL; +} /* findItem() */ + + +/*** hashString - compute hash table index from string + * + * Entry: + * psz - String to compute hash function over + * + * Exit: + * Returns hash table index (0..cHASH_ENTRIES-1). + * + * Algorithm: + * We start out with a zero hash index. For each character in + * the string, we rotate the previous 11 bit hash index left 3 + * bits, and exclusive or in the new character: + * + * 09876543210 + * ----------- + * aaabbbbbbbb 1st byte + * bbbbbbbbaaa Rotate left 3 bits + * bbbxxxxxxxx XOR with 2nd byte + * xxxxxxxxbbb Rotate left 3 bits + * xxxyyyyyyyy XOR with 3rd byte + * ... + * + * This is designed to give us a good spread for ASCII strings. + */ +int hashString(char *psz) { + unsigned index; // Avoid sign extension! + + index = (BYTE)*psz++; // Skip rotate first time + while (*psz) { + //** Rotate 11-bit value left 3 bits + index = ((index & ~HASH_BOT_MASK) >> cHASH_BOT_BITS) | + ((index & HASH_BOT_MASK) << cHASH_TOP_BITS); + index ^= (BYTE)*psz++; + } + + //** Check range + Assert((0<=index) && (index<cHASH_ENTRIES)); + return (int)index; // Match return type +} /* hashString() */ + + +/*** hashItem - Hash item into hash table for generic list + * + * Entry: + * pglist - Generic list + * pgen - Item in list, needs to be hashed into hash table + * + * Exit: + * None. Item hashed into table + */ +void hashItem(PGENLIST pglist, PGENERIC pgen) +{ + int i; + + AssertGList(pglist); + AssertGen(pgen); + + //** Find the hash table entry + if (pgen->pszKey) { + i = hashString(pgen->pszKey); + } + else { + // Doesn't make sense to do searching if some items have no key! + Assert(0); + } + + //** Add item to front of hash table list for this index + AssertHT(pglist->pht); + pgen->pgenHash = pglist->pht->apgen[i]; // Point to previous head + pglist->pht->apgen[i] = pgen; // New head of this hash list +} /* hashItem() */ diff --git a/private/windows/diamond/glist.h b/private/windows/diamond/glist.h new file mode 100644 index 000000000..6d721a62d --- /dev/null +++ b/private/windows/diamond/glist.h @@ -0,0 +1,296 @@ +/*** glist.h - Definitions for Generic List Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 05-Apr-1994 bens Initial version + * 06-Apr-1994 bens Renamed from function manager + * 11-Apr-1994 bens Add pszDesc + * 22-Apr-1994 bens Add GLFindNext, GLSetValue, GLGetValue + * + * Exported Functions: + */ + +#ifndef INCLUDED_GLIST +#define INCLUDED_GLIST 1 + +#include "types.h" +#include "asrt.h" +#include "error.h" + + +/*** ERRGLIST_Xxxx - GLIST error codes + * + */ +#define ERRGLIST_BASE 0x0100 + +#define ERRGLIST_NOT_UNIQUE (ERRGLIST_BASE+1) + + +typedef void * HGENLIST; /* hglist - generic list */ +typedef void * HGENERIC; /* hgen - generic item handle */ + + +/*** PFNGLDESTROYVALUE - Function type to destroy HGENERIC values +/*** FNGLDESTROYVALUE - Macro to help define PFNGLDESTROYVALUE functions + * + * Entry: + * pv - Value to destroy + * + * Exit: + * Value is destroyed. + */ +typedef void (*PFNGLDESTROYVALUE)(void *pv); /* pfngldv */ +#define FNGLDESTROYVALUE(fn) void fn(void *pv) + + +/*** PFNGLDUPLICATEVALUE - Function type to duplicate HGENERIC values +/*** FNGLDUPLICATEVALUE - Macro to help define PFNGLDUPLICATEVALUE functions + * + * Entry: + * pv - Value to duplicate + * + * Exit: + * Returns duplicated value. + */ +typedef void * (*PFNGLDUPLICATEVALUE)(void *pv); /* pfngldup */ +#define FNGLDUPLICATEVALUE(fn) void * fn(void *pv) + + +/*** GLAdd - Add an item to a list + * + * Entry: + * hglist - List to augment + * pszKey - Item key value (may be NULL) + * pv - Item value + * pszDesc - Item description (for error messages) + * fUnique - TRUE => Key must not already exist; FALSE => dups allowed + * perr - ERROR structure + * + * Exit-Success: + * Returns hgen, item is added to list + * + * Exit-Failure: + * Returns NULL, cannot add item to list + * ERROR structure filled in with details of error: + * perr->code == ERRGLIST_NOT_UNIQUE + * fUnique was TRUE, and pszKey already existed. + * perr->pv = HGENERIC of already existing item; + * Otherwise, some other error (out of memory, etc.) + */ +HGENERIC GLAdd(HGENLIST hglist, + char *pszKey, + void *pv, + char *pszDesc, + BOOL fUnique, + PERROR perr); + + +/*** GLDelete - Delete item from a list + * + * Entry: + * hgen - Item handle + * + * Exit-Success: + * Always works, since hgen is assumed to be valid. + */ +void GLDelete(HGENERIC hgen); + + +/*** GLCreateList - Create a list of + * + * Entry: + * pv - Item value for *default* item (NULL if not supplied) + * pfngldv - Function used to destroy values (NULL if not needed) + * pszDesc - Item description (for error messages) + * perr - ERROR structure + * + * Exit-Success: + * Returns hgenlist; list is created. + * + * Exit-Failure: + * Returns NULL, cannot create list; perror filled in with error. + */ +HGENLIST GLCreateList(void *pv, + PFNGLDESTROYVALUE pfngldv, + char *pszDesc, + PERROR perr); + + +/*** GLCopyToList - Copy items from one list to another + * + * Entry: + * phglistDst - Pointer to list to receive copy of items from hglistSrc + * (list is created if necessary). + * hglistSrc - Source list for copy (if NULL, *phglistDst is not + * modified, and this function returns immediately). + * pfngldup - Function used to dupliate item values (NULL if not needed) + * pszDesc - Item description (for error messages) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; hglistDst created if necessary, and items copied from + * hglistSrc to hglistDst; + * + * Exit-Failure: + * Returns FALSE; perror filled in with error. + * + * NOTES: + * If an item with the same key exists in both hglistSrc and hglistDst, + * then the item is *not* copied to hglistDst. + */ +BOOL GLCopyToList(HGENLIST *phglistDst, + HGENLIST hglistSrc, + PFNGLDUPLICATEVALUE pfngldup, + char *pszDesc, + PERROR perr); + + +/*** GLDestroyList - Destroy a list + * + * Entry: + * hglist - List to destroy + * + * Exit-Success: + * Always works, since hglist is assumed to be valid. + */ +void GLDestroyList(HGENLIST hglist); + + +/*** GLFind - Search for item in list + * + * Entry: + * hglist - Generic list + * pszKey - Key to look for (case-insensitive search) + * pszDesc - Item description (for error messages) + * perr - ERROR structure + * + * Exit-Success: + * Returns hgen, if item exists. + * + * Exit-Failure: + * Returns NULL, item does not exist; ERROR structure filled. + */ +HGENERIC GLFind(HGENLIST hglist, + char *pszKey, + char *pszDesc, + PERROR perr); + + +/*** GLFindNext - Search for next item in list + * + * Entry: + * hgen - Start search at *next* item after this item + * pszKey - Key to look for (case-insensitive search) + * + * Exit-Success: + * Returns hgen, if item exists. + * + * Exit-Failure: + * Returns NULL, item does not exist; + */ +HGENERIC GLFindNext(HGENERIC hgen, + char *pszKey); + + +/*** GLFirstItem - Get first item from a list + * + * Entry: + * hglist - List + * + * Exit-Success: + * Returns HGENERIC of first item in list. + * + * Exit-Failure: + * Returns NULL; hglist is bad or empty. + */ +HGENERIC GLFirstItem(HGENLIST hglist); + + +/*** GLNextItem - Get next item + * + * Entry: + * hgen - Item handle + * + * Exit-Success: + * Returns HGENERIC of next item following hgen in list. + * + * Exit-Failure: + * Returns NULL; no more items, or hgen is bad. + */ +HGENERIC GLNextItem(HGENERIC hgen); + + +/*** GLPreviousItem - Get previous item + * + * Entry: + * hgen - Item handle + * + * Exit-Success: + * Returns HGENERIC of item immediately preceding hgen. + * + * Exit-Failure: + * Returns NULL; no more items, or hgen is bad. + */ +HGENERIC GLPreviousItem(HGENERIC hgen); + + +/*** GLGetKey - Get the item key + * + * Entry: + * hgen - Item handle + * + * Exit: + * Returns pointer to item key (will be NULL if GLAdd() received NULL) + */ +char *GLGetKey(HGENERIC hgen); + + +/*** GLGetValue - Get the item value for a particular item + * + * Entry: + * hgen - Item handle + * + * Exit: + * Returns value of item; + */ +void *GLGetValue(HGENERIC hgen); + + +/*** GLSetValue - Set the item value for a particular item + * + * Entry: + * hgen - Item handle + * pv - Item value + * + * Exit: + * None. + */ +void GLSetValue(HGENERIC hgen, void *pv); + + +/*** GLFindAndGetValue - Get the item value for a particular item + * + * Entry: + * hglist - Generic list (may be NULL, in which case returns NULL) + * pszKey - Key to look for (case-insensitive search); + * Pass NULL to get default value; + * + * Exit-Success: + * Returns value of item if key exists. + * If the key was not found, but a default value was supplied on the + * GLGLCreateList() call, then that default value is returned. + * + * Exit-Failure: + * Returns NULL, item does not exist (key not found and no default + * value was supplied on the GLCreateList() call). + */ +void *GLFindAndGetValue(HGENLIST hglist, + char *pszKey); + +#endif // INCLUDED_GLIST diff --git a/private/windows/diamond/glist.msg b/private/windows/diamond/glist.msg new file mode 100644 index 000000000..68a5fc024 --- /dev/null +++ b/private/windows/diamond/glist.msg @@ -0,0 +1,21 @@ +/*** glist.msg - Displayable strings for glist.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 11-Apr-1994 bens Initial version + * 27-Apr-1994 bens Error for duplicating + */ + +//** Error Messages + +#define pszGLERR_ALREADY_CREATED "%1 already exists: %2" +#define pszGLERR_OUT_OF_MEMORY_ITEM "Out of memory adding %1: %2" +#define pszGLERR_OUT_OF_MEMORY_LIST "Out of memory creating %1 list" +#define pszGLERR_OUT_OF_MEMORY_DUP "Out of memory duplicating %1" +#define pszGLERR_ITEM_NOT_FOUND "Undefined %1: %2" diff --git a/private/windows/diamond/inf.c b/private/windows/diamond/inf.c new file mode 100644 index 000000000..8377609d3 --- /dev/null +++ b/private/windows/diamond/inf.c @@ -0,0 +1,2738 @@ +/*** inf.c - Diamond INF generation routines + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 23-Feb-1994 bens Initial version + * 24-Feb-1994 bens Use new tempfile routines + * 01-Mar-1994 bens Generate header and footer + * 02-Mar-1994 bens Add function header comments + * 02-May-1994 bens Add customizable INF stuff + * 04-Jun-1994 bens Add Version/Language support + * 12-Jul-1994 bens InfHeader/Footer are now customizable; support + * overriding disk/cabinet/file formats for + * specific disk/cabinet/file numbers. + * 28-Mar-1995 jeffwe Add ChecksumWidth variable + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <malloc.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <dos.h> // Get file attribute definitions +#include <errno.h> +#include <direct.h> +#include <time.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" +#include "fileutil.h" +#include "variable.h" +#include "misc.h" +#include "format.h" + +#include "inf.h" +#include "inf.msg" + +#include "dfparse.msg" + + +/*****************************/ +/*** DEFINITIONS and TYPES ***/ +/*****************************/ + + +#define cbINF_IO_BUFFER 2048 // INF I/O buffer size + + +//** Don't redefine definitions in Win16 WINDOWS.H (_INC_WINDOWS) +// or Win32 WINDOWS.H (_WINDOWS_) +// +#if !defined(_INC_WINDOWS) && !defined(_WINDOWS_) +#define HIWORD(l) ((WORD)(l>>16)) +#define LOWORD(l) ((WORD)(l&0xFFFF)) +#endif // _INC_WINDOWS + + +/*** yearFAT_DATE_MIN/MAX - minimum and maximum FAT Date years + * + */ +#define yearFAT_DATE_MIN 1980 +#define yearFAT_DATE_MAX 2107 + + +/** INF definitions + * + */ +typedef enum { + itmpDISKS, // Disk list tempfile + itmpCABINETS, // Cabinet list tempfile + itmpFILES, // File list tempfile + cTMP_FILES // Count of tempfiles +} ETMPFILES; /* etmp */ + + +#ifdef ASSERT +#define sigINF MAKESIG('I','N','F','$') // INF signature +#define AssertInf(pinf) AssertStructure(pinf,sigINF); +#else // !ASSERT +#define AssertInf(pinf) +#endif // !ASSERT + +typedef struct { /* inf */ +#ifdef ASSERT + SIGNATURE sig; // structure signature sigINF +#endif + HTEMPFILE ahtmp[cTMP_FILES]; // Temporary files + char achLine[cbINF_LINE_MAX]; // Line output formatting buffer +} INF; +typedef INF *PINF; /* pinf */ + +#define PINFfromHINF(hinf) ((PINF)(hinf)) +#define HINFfromPINF(pinf) ((HINF)(pinf)) + + +/*** TMPFILEINIT - info used to initialize INF temporary files + * + */ +typedef struct { + char *pszDesc; // Description of tempfile + char *pszHeaderVar; // Header line variable + char *pszHeaderVarTemplate; // Header line variable template +} TMPFILEINIT; /* tfi */ + + +/* + * Avoid chicken & egg definition problem! + */ +typedef struct SASCONTEXT_t *PSASCONTEXT; /* psascon */ + + +/*** PFNFPFORMAT - Function type for FILEPARM formatting + *** FNFPFORMAT - Macro to help define FILEPARM formatting function + * + * Entry: + * psess - Session + * pszDst - Buffer to receive formatted string + * cbDst - Size of pszDst buffer + * pszName - Parameter Name + * psascon - SASCONTEXT pointer + * perr - ERROR structure + * + * Exit-Success: + * Returns length of string written to pszDst (excluding NUL byte) + * + * Exit-Failure: + * Returns -1; perr filled in. + */ +typedef BOOL (*PFNFPFORMAT)(PSESSION psess, + char *pszDst, + int cbDst, + char *pszName, + PSASCONTEXT psascon, + PERROR perr); /* pfnfpf */ +#define FNFPFORMAT(fn) BOOL fn(PSESSION psess, \ + char *pszDst, \ + int cbDst, \ + char *pszName, \ + PSASCONTEXT psascon, \ + PERROR perr) + + +/*** PFNFPVALIDATE - Function type for FILEPARM validation + *** FNFPVALIDATE - macro to help define FILEPARM validation function + * + * Entry: + * psess - Session + * pszName - Parameter Name + * pszValue - Value to check for validity; + * pv - Parameter-specific context + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, value is valid. + * pv updated with interpreted value, if appropriate. + * + * Exit-Failure: + * Returns FALSE, perr filled in. + */ +typedef BOOL (*PFNFPVALIDATE)(PSESSION psess, + char *pszName, + char *pszValue, + void *pv, + PERROR perr); /* pfnfpv */ +#define FNFPVALIDATE(fn) BOOL fn(PSESSION psess, \ + char *pszName, \ + char *pszValue, \ + void *pv, \ + PERROR perr) + + +/*** VALIDATEFILEPARM - validate file parameter + * + */ +typedef struct { + char *pszName; // Parameter Name + PFNFPVALIDATE pfnfpv; // Validation function +} VALIDATEFILEPARM; /* vfp */ +typedef VALIDATEFILEPARM *PVALIDATEFILEPARM; /* pvfp */ + + +/*** FORMATFILEPARM - Format file parameter + * + */ +typedef struct { + char *pszName; // Parameter Name + PFNFPFORMAT pfnfpf; // Validation function +} FORMATFILEPARM; /* ffp */ +typedef FORMATFILEPARM *PFORMATFILEPARM; /* pffp */ + + +/*** SASCONTEXT - Subcontext for formatting INF file lines + * + */ +#ifdef ASSERT +#define sigSASCONTEXT MAKESIG('S','A','S','C') // SASCONTEXT signature +#define AssertSascon(p) AssertStructure(p,sigSASCONTEXT); +#else // !ASSERT +#define AssertSascon(p) +#endif // !ASSERT + +typedef struct SASCONTEXT_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigSASCONTEXT +#endif + PSESSION psess; // Session + HGENLIST hglistParm; // Parameter list (if any) + PFORMATFILEPARM pffp; // Parm->function mapping table + void *pv; // Type-specific context pointer +} SASCONTEXT; /* sascon */ +typedef SASCONTEXT *PSASCONTEXT; /* psascon */ + + +/*** FILECONTEXT - Subcontext for formatting INF file lines + * + */ +#ifdef ASSERT +#define sigFILECONTEXT MAKESIG('F','C','O','N') // FILECONTEXT signature +#define AssertFilecon(p) AssertStructure(p,sigFILECONTEXT); +#else // !ASSERT +#define AssertFilecon(p) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFILECONTEXT +#endif + char *pszFile; // destination file name + PFILEINFO pfinfo; // file info +} FILECONTEXT; /* filecon */ +typedef FILECONTEXT *PFILECONTEXT; /* pfilecon */ + + +/*** CABCONTEXT - Subcontext for formatting INF cabinet lines + * + */ +#ifdef ASSERT +#define sigCABCONTEXT MAKESIG('C','C','O','N') // CABCONTEXT signature +#define AssertCabcon(p) AssertStructure(p,sigCABCONTEXT); +#else // !ASSERT +#define AssertCabcon(p) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigCABCONTEXT +#endif + int iDisk; // Disk where cabinet resides + int iCabinet; // Cabinet number + char *pszCab; // Cabinet file name +} CABCONTEXT; /* cabcon */ +typedef CABCONTEXT *PCABCONTEXT; /* pcabcon */ + + +/*** DISKCONTEXT - Subcontext for formatting INF DISK lines + * + */ +#ifdef ASSERT +#define sigDISKCONTEXT MAKESIG('D','C','O','N') // DISKCONTEXT signature +#define AssertDiskcon(p) AssertStructure(p,sigDISKCONTEXT); +#else // !ASSERT +#define AssertDiskcon(p) +#endif // !ASSERT + +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigDISKCONTEXT +#endif + int iDisk; // Disk number + char *pszDisk; // Disk label +} DISKCONTEXT; /* diskcon */ +typedef DISKCONTEXT *PDISKCONTEXT; /* pdiskcon */ + + + +/******************/ +/*** PROTOTYPES ***/ +/******************/ + +BOOL catTempFile(FILE * pfileDst, + char * pszFile, + HTEMPFILE htmp, + char * pszMode, + PERROR perr); +BOOL checkInfLineFormats(PSESSION psess, PERROR perr); +BOOL doHeaderFooter(PSESSION psess, + FILE *pfileInf, + char *pszInfFile, + char *pszVarMain, + char *pszVarTemplate, + char *pszComment, + char *pszTime, + char *pszVer, + PERROR perr); +HVARIABLE GetInfCustomVar(PSESSION psess,char *pszParm,BOOL fCopy,PERROR perr); +char * getLineFormat(PSESSION psess, + char *pszVarMain, + char *pszVarTemplate, + int i); +BOOL infClose(HINF hinf, PERROR perr); + +WORD FATAttrFromPsz(char *pszAttr, PERROR perr); +WORD FATDateFromPsz(char *pszDate, PERROR perr); +WORD FATTimeFromPsz(char *pszTime, PERROR perr); +int pszFromFATAttr(char *pszDst, int cbDst, WORD attr); +int pszFromFATDate(char *pszDst, int cbDst, WORD date, BOOL fMMDDYY); +int pszFromFATTime(char *pszDst, int cbDst, WORD time); + +FNFORMATPARM(fnfpInfLine); + +FNFPFORMAT(fnfpfCabinetFileName); +FNFPFORMAT(fnfpfCabinetNumber); +FNFPFORMAT(fnfpfDiskNumber); +FNFPFORMAT(fnfpfDiskLabel); +FNFPFORMAT(fnfpfFileChecksum); +FNFPFORMAT(fnfpfFileAttr); +FNFPFORMAT(fnfpfFileDate); +FNFPFORMAT(fnfpfFileLanguage); +FNFPFORMAT(fnfpfFileName); +FNFPFORMAT(fnfpfFileNumber); +FNFPFORMAT(fnfpfFileSize); +FNFPFORMAT(fnfpfFileTime); +FNFPFORMAT(fnfpfFileVerNum); +FNFPFORMAT(fnfpfFileVerString); + +FNFPVALIDATE(fnfpvBOOL); +FNFPVALIDATE(fnfpvFileAttr); +FNFPVALIDATE(fnfpvFileDate); +FNFPVALIDATE(fnfpvFileTime); +FNFPVALIDATE(fnfpvLong); +FNFPVALIDATE(fnfpvShort); +FNFPVALIDATE(fnfpvString); + + +/*****************/ +/*** VARIABLES ***/ +/*****************/ + + +/*** atfi - array of INF temporary file information + * + * NOTE: Must be in same order as ETMPFILES enumeration! + */ +TMPFILEINIT atfi[] = { + {pszINF_TMP_FILED,pszVAR_INF_DISK_HEADER,pszPATTERN_VAR_INF_DISK_HEADER}, // itmpDISKS + {pszINF_TMP_FILEC,pszVAR_INF_CAB_HEADER ,pszPATTERN_VAR_INF_CAB_HEADER }, // itmpCABINETS + {pszINF_TMP_FILEF,pszVAR_INF_FILE_HEADER,pszPATTERN_VAR_INF_FILE_HEADER}, // itmpFILES +}; + + +/*** avfpInfVar - InfXxx variable validation mapping + * + * NOTE: This table is used by CheckForInfVarOverrides() to pick up + * an InfXxx variable overrides. + */ +VALIDATEFILEPARM avfpInfVar[] = { + {pszPARM_FILEATTR , fnfpvFileAttr }, // File attributes + {pszPARM_FILEDATE , fnfpvFileDate }, // File date + {pszPARM_FILETIME , fnfpvFileTime }, // File time + {NULL , NULL } // end-of-list +}; + + +/*** avfpFile - File Parm validation mapping + * + */ +VALIDATEFILEPARM avfpFile[] = { + {pszPARM_CAB_NUMBER , fnfpvShort }, // Cabinet number + {pszPARM_CHECKSUM , fnfpvLong }, // Checksum + {pszPARM_DISK_NUMBER, fnfpvShort }, // Disk number + {pszPARM_FILEATTR , fnfpvFileAttr }, // File attributes + {pszPARM_FILEDATE , fnfpvFileDate }, // File date + {pszPARM_FILENAME , fnfpvString }, // File name + {pszPARM_FILE_NUMBER, fnfpvLong }, // File number + {pszPARM_FILESIZE , fnfpvLong }, // File size + {pszPARM_FILETIME , fnfpvFileTime }, // File time + {pszPARM_INF , fnfpvBOOL }, // /INF=YES|NO + {pszPARM_LANG , fnfpvString }, // VER.DLL language code + {pszPARM_UNIQUE , fnfpvBOOL }, // /UNIQUE=YES|NO + {pszPARM_VERNUM , fnfpvString }, // VER.DLL version *number* + {pszPARM_VERSTR , fnfpvString }, // VER.DLL version *string* + {NULL , NULL } // end-of-list +}; + + +/*** affpFile - File Parm formatting mapping + * + */ +FORMATFILEPARM affpFile[] = { + {pszPARM_CAB_NUMBER , fnfpfCabinetNumber}, // Cabinet number + {pszPARM_CHECKSUM , fnfpfFileChecksum }, // Checksum + {pszPARM_DISK_NUMBER, fnfpfDiskNumber }, // Disk number + {pszPARM_FILEATTR , fnfpfFileAttr }, // File attributes + {pszPARM_FILEDATE , fnfpfFileDate }, // File date + {pszPARM_FILENAME , fnfpfFileName }, // File name + {pszPARM_FILE_NUMBER, fnfpfFileNumber }, // File number + {pszPARM_FILESIZE , fnfpfFileSize }, // File size + {pszPARM_FILETIME , fnfpfFileTime }, // File time + {pszPARM_LANG , fnfpfFileLanguage }, // VER.DLL language code + {pszPARM_VERNUM , fnfpfFileVerNum }, // VER.DLL version *number* + {pszPARM_VERSTR , fnfpfFileVerString}, // VER.DLL version *string* + {NULL , NULL } // end-of-list +}; + + +/*** affpDisk - Disk Parm formatting mapping + * + */ +FORMATFILEPARM affpDisk[] = { + {pszPARM_DISK_NUMBER, fnfpfDiskNumber }, // Disk number + {pszPARM_LABEL , fnfpfDiskLabel }, // Disk label + {NULL , NULL } // end-of-list +}; + + +/*** affpCabinet - Cabinet Parm formatting mapping + * + */ +FORMATFILEPARM affpCabinet[] = { + {pszPARM_CAB_NUMBER , fnfpfCabinetNumber }, // Cabinet number + {pszPARM_CAB_FILE , fnfpfCabinetFileName}, // Cabinet file name + {pszPARM_DISK_NUMBER, fnfpfDiskNumber }, // Disk number + {NULL , NULL } // end-of-list +}; + + + +/*****************/ +/*** FUNCTIONS ***/ +/*****************/ + + +/*** infCreate - Create INF context + * + * NOTE: See inf.h for entry/exit conditions. + */ +HINF infCreate (PSESSION psess,PERROR perr) +{ + char achVar[cbVAR_NAME_MAX]; // Header line variable names + char ch; + HVARIABLE hvar; + int itmp; + int iLine; + PINF pinf; + FILE *pfile; + char *pszLine; + char *pszOrder; + + AssertSess(psess); + + //** Create INF structure + if (!(pinf = MemAlloc(sizeof(INF)))) { + ErrSet(perr,pszINFERR_OUT_OF_MEMORY); + return NULL; + } + + //** Initialize so error cleanup is simple + for (itmp=0; itmp<cTMP_FILES; itmp++) { + pinf->ahtmp[itmp] = NULL; + } + SetAssertSignature(pinf,sigINF); // Set so we can use infDestroy() + psess->hinf = HINFfromPINF(pinf); // Set for checkInfLineFormat + + //** Make sure there are no undefined parameters in any of the + // InfXxxLineFormat variables! + if (!checkInfLineFormats(psess,perr)) { + return FALSE; + } + + //** Create temp files + hvar = VarFind(psess->hvlist,pszVAR_INF_SECTION_ORDER,perr); + Assert(!perr->fError); // Must be defined + pszOrder = VarGetString(hvar); // Section order string + Assert(strlen(pszOrder) <= cTMP_FILES); + for ( ; *pszOrder; pszOrder++) { + //** Pick temp file + ch = toupper(*pszOrder); + switch (ch) { + case pszISO_DISK: itmp = itmpDISKS; break; + case pszISO_CABINET: itmp = itmpCABINETS; break; + case pszISO_FILE: itmp = itmpFILES; break; + + default: + Assert(0); + return FALSE; + } + + //** Get required header line + hvar = VarFind(psess->hvlist,atfi[itmp].pszHeaderVar,perr); + Assert(!perr->fError); // Must be defined + pszLine = VarGetString(hvar); + + //** Create temp file + pinf->ahtmp[itmp] = TmpCreate(atfi[itmp].pszDesc, // description + pszINF_TMP_PREFIX, // filename prefix + "wt", // write text mode + perr); + if (!pinf->ahtmp[itmp]) { + goto error; + } + + //** Add header line(s) + pfile = TmpGetStream(pinf->ahtmp[itmp],perr); + Assert(pfile!=NULL); + iLine = 1; + while (pszLine) { + if (fprintf(pfile,"%s\n",pszLine) < 0) { // Add header line + goto error; + } + + //** Get next header line (if any) + if (!nameFromTemplate(achVar, + sizeof(achVar), + atfi[itmp].pszHeaderVarTemplate, + iLine, + pszINF_HEADER_TEMPLATE, + perr)) { + goto error; + } + hvar = VarFind(psess->hvlist,achVar,perr); + if (hvar) { // Get line + pszLine = VarGetString(hvar); + iLine++; // Next variable to get + } + else { // Done + ErrClear(perr); // Not an error + pszLine = NULL; // End loop + } + } + } + + //** Success + psess->fGenerateInf = TRUE; // Remember we are creating INF + return HINFfromPINF(pinf); + +error: + infDestroy(pinf,perr); + return NULL; +} /* infCreate() */ + + +/*** infAddLine - Add a line to an INF area + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infAddLine(HINF hinf, INFAREA inf, char *pszLine, PERROR perr) +{ + FILE *pfile; + PINF pinf; + int itmp; + + pinf = PINFfromHINF(hinf); + AssertInf(pinf); + + //** Select correct temp file + switch (inf) { + case infDISK: itmp = itmpDISKS; break; + case infCABINET: itmp = itmpCABINETS; break; + case infFILE: itmp = itmpFILES; break; + + default: + Assert(0); + return FALSE; + } + + //** Write line to temp file + pfile = TmpGetStream(pinf->ahtmp[itmp],perr); + Assert(pfile!=NULL); + return fprintf(pfile,"%s\n",pszLine) > 0; +} /* infAddLine() */ + + +/*** infDestroy - Destroy INF context + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infDestroy(HINF hinf, PERROR perr) +{ + int i; + PINF pinf; + + pinf = PINFfromHINF(hinf); + AssertInf(pinf); + + //** Make sure files are closed + if (!infClose(pinf,perr)) { + return FALSE; + } + + //** Get rid of tempfiles + for (i=0; i<cTMP_FILES; i++) { + if (pinf->ahtmp[i]) { + if (!TmpDestroy(pinf->ahtmp[i],perr)) { + return FALSE; + } + pinf->ahtmp[i] = NULL; + } + } + + //** Free INF structure + ClearAssertSignature(pinf); + MemFree(pinf); + return TRUE; +} /* infDestroy() */ + + +/*** infClose - Close file handles in INF context + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infClose(HINF hinf, PERROR perr) +{ + int i; + PINF pinf; + + pinf = PINFfromHINF(hinf); + AssertInf(pinf); + + //** Close all temp files + for (i=0; i<cTMP_FILES; i++) { + if (pinf->ahtmp[i]) { + if (!TmpClose(pinf->ahtmp[i],perr)) { + return FALSE; + } + } + } + return TRUE; +} /* infClose() */ + + +/*** infGenerate - Generate INF file + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infGenerate(PSESSION psess, + char *pszInfFile, + time_t *ptime, + char *pszVer, + PERROR perr) +{ + char achTime[50]; + char ch; + HVARIABLE hvar; + int itmp; + FILE *pfileInf; + PINF pinf; + char *pszOrder; + char *pszComment; + + AssertSess(psess); + pinf = PINFfromHINF(psess->hinf); + AssertInf(pinf); + + //** Flush temporary files + if (!infClose(pinf,perr)) { + return FALSE; + } + + //** Create final output file + pfileInf = fopen(pszInfFile,"wb"); + if (!pfileInf) { + ErrSet(perr,pszINFERR_CANT_CREATE_INF,"%s",pszInfFile); + return FALSE; + } + + //** Get INF line comment prefix + hvar = VarFind(psess->hvlist,pszVAR_INF_COMMENT_STRING,perr); + Assert(!perr->fError); // Must be defined + pszComment = VarGetString(hvar); + + //** Trim off trailing new-line in time + strcpy(achTime,ctime(ptime)); + achTime[strlen(achTime)-1] = '\0'; + + //** Add header + if (!doHeaderFooter(psess, + pfileInf, + pszInfFile, + pszVAR_INF_HEADER, + pszPATTERN_VAR_INF_HEADER, + pszComment, + achTime, + pszVer, + perr)) { + goto error; + } + + //** Concatenate intermediate files + hvar = VarFind(psess->hvlist,pszVAR_INF_SECTION_ORDER,perr); + Assert(!perr->fError); // Must be defined + pszOrder = VarGetString(hvar); // Section order string + Assert(strlen(pszOrder) <= cTMP_FILES); + for ( ; *pszOrder; pszOrder++) { + //** Pick temp file + ch = toupper(*pszOrder); + switch (ch) { + case pszISO_DISK: itmp = itmpDISKS; break; + case pszISO_CABINET: itmp = itmpCABINETS; break; + case pszISO_FILE: itmp = itmpFILES; break; + + default: + Assert(0); + return FALSE; + } + //** Concatenate it + Assert(pinf->ahtmp[itmp] != NULL); + if (!catTempFile(pfileInf,pszInfFile,pinf->ahtmp[itmp],"rb",perr)) { + goto error; + } + } + + //** Add footer + if (!doHeaderFooter(psess, + pfileInf, + pszInfFile, + pszVAR_INF_FOOTER, + pszPATTERN_VAR_INF_FOOTER, + pszComment, + achTime, + pszVer, + perr)) { + goto error; + } + + //** Success + fclose(pfileInf); + return TRUE; + +error: + fclose(pfileInf); + return FALSE; +} /* infGenerate() */ + + +/*** doHeaderFooter - Add header or footer to INF file + * + * Entry: + * psess - SESSION + * pfileInf - File pointer for INF file (opened in mode "rb", so + * we have to do \r\n for newline). + * pszInfFile - INF file name (for error messages) + * pszVarMain - Main variable name (InfHeader or InfFooter) + * pszVarTemplate - Template for other variables (InfHeader* or InfFooter*) + * pszComment - Line comment string (usually ";") + * pszTime - Time string + * pszVer - Diamond version string + * perr - ERROR + * Exit-Success: + * Returns TRUE, lines added to INF file + * + * Exit-Failure: + * Returns FALSE, perr filled in. + * + * NOTE: If the variable pszVarMain has the empty string value, no lines + * are added to the INF file. + */ +BOOL doHeaderFooter(PSESSION psess, + FILE *pfileInf, + char *pszInfFile, + char *pszVarMain, + char *pszVarTemplate, + char *pszComment, + char *pszTime, + char *pszVer, + PERROR perr) +{ + char achVar[cbVAR_NAME_MAX]; // Header line variable names + HVARIABLE hvar; + int iLine; + PINF pinf; + char *psz; + char *pszVar; + + AssertSess(psess); + + //** See if we're supposed to add lines to file + hvar = VarFind(psess->hvlist,pszVarMain,perr); + Assert(!perr->fError); // Must be defined + psz = VarGetString(hvar); // Get value + if (strlen(psz) == 0) { // Main variable empty + return TRUE; // Skip writing to INF + } + + //** Add lines to INF file + pinf = PINFfromHINF(psess->hinf); + AssertInf(pinf); + iLine = 1; + pszVar = pszVarMain; + while (psz) { + //** Do substitution of comment string, time, and version + MsgSet(pinf->achLine,psz,"%s%s%s",pszComment,pszTime,pszVer); + + //** Add line to INF file (opened in BINARY, so need \r\n!) + if (fprintf(pfileInf,"%s\r\n",pinf->achLine) < 0) { + ErrSet(perr,pszINFERR_INF_WRITE,"%s%s",pszVar,pszInfFile); + return FALSE; + } + + //** Get next header line (if any) + if (!nameFromTemplate(achVar, + sizeof(achVar), + pszVarTemplate, + iLine, + pszINF_HEADER_TEMPLATE, + perr)) { + return FALSE; // perr already filled in + } + hvar = VarFind(psess->hvlist,achVar,perr); + if (hvar) { // Get line + psz = VarGetString(hvar); + iLine++; // Next variable to get + } + else { // Done + ErrClear(perr); // Not an error + psz = NULL; // End loop + } + } + + //** Success + return TRUE; +} /* doHeaderFooter() */ + + +/*** infAddDisk - Add information for a new disk to the INF context + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infAddDisk(PSESSION psess, int iDisk, char *pszDisk, PERROR perr) +{ + DISKCONTEXT diskcon; + FILE *pfile; + PINF pinf; + char *pszFmt; + SASCONTEXT sascon; + + AssertSess(psess); + pinf = PINFfromHINF(psess->hinf); + AssertInf(pinf); + + //** Quick out if not generating this INF area + if (!pinf->ahtmp[itmpDISKS]) { + return TRUE; + } + + Assert(iDisk != idiskBAD); + + //** Get line format + pszFmt = getLineFormat(psess, + pszVAR_INF_DISK_LINE_FMT, + pszPATTERN_VAR_INF_DISK_LINE_FMT, + iDisk); + + //** Format and write line to file + SetAssertSignature((&diskcon),sigDISKCONTEXT); + diskcon.iDisk = iDisk; + diskcon.pszDisk = pszDisk; + + SetAssertSignature((&sascon),sigSASCONTEXT); + sascon.psess = psess; + sascon.hglistParm = NULL; + sascon.pffp = affpDisk; + sascon.pv = &diskcon; + + if (!SubstituteFormatString(pinf->achLine, + sizeof(pinf->achLine), + pszFmt, + fnfpInfLine, + &sascon, + perr)) { + return FALSE; + } + + pfile = TmpGetStream(pinf->ahtmp[itmpDISKS],perr); + Assert(pfile!=NULL); + if (fprintf(pfile,"%s\n",pinf->achLine) < 0) { + return FALSE; + } + return TRUE; +} /* infAddDisk() */ + + +/*** infAddCabinet - Add information for a new cabinet to the INF context + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infAddCabinet(PSESSION psess, + int iCabinet, + int iDisk, + char *pszCab, + PERROR perr) +{ + CABCONTEXT cabcon; + FILE *pfile; + PINF pinf; + char *pszFmt; + SASCONTEXT sascon; + + AssertSess(psess); + pinf = PINFfromHINF(psess->hinf); + AssertInf(pinf); + + //** Quick out if not generating this INF area + if (!pinf->ahtmp[itmpCABINETS]) { + return TRUE; + } + + Assert(iCabinet != icabBAD); + Assert(iDisk != idiskBAD); + + //** Get line format + pszFmt = getLineFormat(psess, + pszVAR_INF_CAB_LINE_FMT, + pszPATTERN_VAR_INF_CAB_LINE_FMT, + iCabinet); + + //** Format and write line to file + SetAssertSignature((&cabcon),sigCABCONTEXT); + cabcon.iCabinet = iCabinet; + cabcon.iDisk = iDisk; + cabcon.pszCab = pszCab; + + SetAssertSignature((&sascon),sigSASCONTEXT); + sascon.psess = psess; + sascon.hglistParm = NULL; + sascon.pffp = affpCabinet; + sascon.pv = &cabcon; + + if (!SubstituteFormatString(pinf->achLine, + sizeof(pinf->achLine), + pszFmt, + fnfpInfLine, + &sascon, + perr)) { + return FALSE; + } + + pfile = TmpGetStream(pinf->ahtmp[itmpCABINETS],perr); + Assert(pfile!=NULL); + if (fprintf(pfile,"%s\n",pinf->achLine) < 0) { + return FALSE; + } + return TRUE; +} /* infAddCabinet() */ + + +/*** infAddFile - Add information for a new file to the INF context + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infAddFile(PSESSION psess, + char *pszFile, + PFILEINFO pfinfo, + HGENLIST hglistParm, + PERROR perr) +{ + FILECONTEXT filecon; + HGENERIC hgen; + FILE *pfile; + PINF pinf; + PLINEINFO plinfo; + char *pszFmt; + SASCONTEXT sascon; + + AssertSess(psess); + pinf = PINFfromHINF(psess->hinf); + AssertInf(pinf); + + AssertFinfo(pfinfo); + Assert(pfinfo->iDisk != idiskBAD); + Assert(pfinfo->iCabinet != icabBAD); + + //** Quick out if not generating this INF area + if (!pinf->ahtmp[itmpFILES]) { + return TRUE; + } + + //** Get line format + pszFmt = getLineFormat(psess, + pszVAR_INF_FILE_LINE_FMT, + pszPATTERN_VAR_INF_FILE_LINE_FMT, + pfinfo->iFile); + + //** Format and write line to file + SetAssertSignature((&filecon),sigFILECONTEXT); + filecon.pszFile = pszFile; + filecon.pfinfo = pfinfo; + + SetAssertSignature((&sascon),sigSASCONTEXT); + sascon.psess = psess; + sascon.hglistParm = hglistParm; + sascon.pffp = affpFile; + sascon.pv = &filecon; + + if (!SubstituteFormatString(pinf->achLine, + sizeof(pinf->achLine), + pszFmt, + fnfpInfLine, + &sascon, + perr)) { + return FALSE; + } + pfile = TmpGetStream(pinf->ahtmp[itmpFILES],perr); + Assert(pfile!=NULL); + if (fprintf(pfile,"%s\n",pinf->achLine) < 0) { + return FALSE; + } + + //** Remember that we added file to inf + Assert((psess->ddfmode == ddfmodeRELATIONAL) || // Relational mode -or- + !(pfinfo->flags & fifWRITTEN_TO_INF)); // Not yet written to INF + pfinfo->flags |= fifWRITTEN_TO_INF; + + //** Print out any additional INF lines + if (!pfinfo->hglistInfLines) { // No additional lines + return TRUE; + } + for (hgen = GLFirstItem(pfinfo->hglistInfLines); + hgen; + hgen = GLNextItem(hgen)) { + + plinfo = GLGetValue(hgen); // Get line info + AssertLinfo(plinfo); + if (!infAddLine(psess->hinf,plinfo->inf,plinfo->pszLine,perr)) { + return FALSE; + } + } + + //** Free the INF lines, to conserve space + GLDestroyList(pfinfo->hglistInfLines); + pfinfo->hglistInfLines = NULL; // Remember they are gone + + return TRUE; +} /* infAddFile() */ + + +/*** getLineFormat - Get line format for INF detail line + * + * Entry: + * psess - SESSION + * pszVarMain - Main variable name (InfXxxLineFormat) + * pszVarTemplate - Template for override var (InfXxxLineFormat*) + * i - Variable number to search for (replaces * above) + * + * Exit: + * Returns pointer to line format string. + */ +char *getLineFormat(PSESSION psess, + char *pszVarMain, + char *pszVarTemplate, + int i) +{ + char achVar[cbVAR_NAME_MAX]; + ERROR err; + HVARIABLE hvar; + + AssertSess(psess); + + //** See if override format exists + if (!nameFromTemplate(achVar, // Couldn't build template + sizeof(achVar), + pszVarTemplate, + i, + "", + &err) || + !(hvar = VarFind(psess->hvlist,achVar,&err))) // Override not defined + { + //** Use standard format + ErrClear(&err); // Ignore error from above + hvar = VarFind(psess->hvlist,pszVarMain,&err); + Assert(!err.fError); // Must be defined + } + + //** Return the format string + return VarGetString(hvar); +} /* getLineFormat() */ + + +/*** catTempFile - Append a tempfile to an open FILE* stream + * + * Entry: + * pfileDst - file stream to receive temp file + * pszFile - name of pfileDst (for error reporting) + * htmp - tempfile + * pszMode - mode to pass to fopen for tempfile + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; files concatenated, pfileDst still open and at EOF + * + * Exit-Failure: + * Returns FALSE; perr is filled in. + */ +BOOL catTempFile(FILE * pfileDst, + char * pszFile, + HTEMPFILE htmp, + char * pszMode, + PERROR perr) +{ + int cbRead; + int cbWrite; + ERROR errTmp; + char *pbuf=NULL; + FILE *pfileSrc=NULL; + char *pszDesc; + char *pszTmpName; + + Assert(pfileDst != NULL); + Assert(htmp != NULL); + Assert(TmpGetStream(htmp,perr) == NULL); // Should be closed + + //** Get strings for error messages + pszDesc = TmpGetDescription(htmp,&errTmp); + pszTmpName= TmpGetFileName(htmp,&errTmp); + + //** Get I/O buffer + if (!(pbuf = MemAlloc(cbINF_IO_BUFFER))) { + ErrSet(perr,pszINFERR_NO_MEM_CATING_FILE,"%s%s%s", + pszDesc,pszTmpName,pszFile); + return FALSE; + } + + //** Open file to copy from + if (!TmpOpen(htmp,pszMode,perr)) { + goto error; + } + pfileSrc = TmpGetStream(htmp,perr); + Assert(pfileSrc != NULL); // Should be open now + + //** Copy source to destination + while (!feof(pfileSrc)) { + cbRead = fread(pbuf,1,cbINF_IO_BUFFER,pfileSrc); + if (ferror(pfileSrc)) { + ErrSet(perr,pszINFERR_READING,"%s%s%s",pszDesc,pszTmpName,pszFile); + goto error; + } + if (cbRead > 0) { + cbWrite = fwrite(pbuf,1,cbRead,pfileDst); + if (ferror(pfileDst)) { + ErrSet(perr,pszINFERR_WRITING,"%s%s%s", + pszDesc,pszTmpName,pszFile); + goto error; + } + } + } + + //** Clean up + TmpClose(htmp,perr); + MemFree(pbuf); + return TRUE; // Success + +error: + if (!pbuf) { + MemFree(pbuf); + } + if (!pfileSrc) { + fclose(pfileSrc); + } + return FALSE; +} /* catTempFile() */ + + +/*** DestroyFileInfo - Destroy a file info structure + * + * NOTE: see inf.h for entry/exit conditions. + */ +FNGLDESTROYVALUE(DestroyFileInfo) +{ + PFILEINFO pfinfo; + + //** Quick out + if (pv == NULL) { + return; + } + pfinfo = pv; + AssertFinfo(pfinfo); + + //** Free DDF file name + if (pfinfo->pszDDF) { + MemFree(pfinfo->pszDDF); + } + + //** Free parameter list + if (pfinfo->hglistParm != NULL) { + GLDestroyList(pfinfo->hglistParm); + } + + //** Free INF line list + if (pfinfo->hglistInfLines != NULL) { + GLDestroyList(pfinfo->hglistInfLines); + } + + //** Free structure + ClearAssertSignature(pfinfo); + MemFree(pfinfo); +} /* DestroyFileInfo() */ + + +/*** DestroyLineInfo - Destroy a line info structure + * + * NOTE: see inf.h for entry/exit conditions. + */ +FNGLDESTROYVALUE(DestroyLineInfo) +{ + PLINEINFO plinfo; + + //** Quick out + if (pv == NULL) { + return; + } + plinfo = pv; + AssertLinfo(plinfo); + + //** Free line + if (plinfo->pszLine) { + MemFree(plinfo->pszLine); + } + + //** Free structure + ClearAssertSignature(plinfo); + MemFree(plinfo); +} /* DestroyLineInfo() */ + + + + +/*** CheckForInfVarOverrides - Apply any InfXxxx overrides to FILEINFO + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL CheckForInfVarOverrides(PSESSION psess, + PFILEINFO pfinfo, + PERROR perr) +{ + HVARIABLE hvar; + char *pszValue; + PVALIDATEFILEPARM pvfp; + + AssertSess(psess); + AssertFinfo(pfinfo); + + //** See if any overrides are defined for standard parameters + for (pvfp=avfpInfVar; pvfp->pszName; pvfp++) { + //** See if variable is defined (don't clone to pass 2 var list) + hvar = GetInfCustomVar(psess,pvfp->pszName,FALSE,perr); + if (hvar) { // Variable is defined + pszValue = VarGetString(hvar); + Assert(pszValue != NULL); + //** Validate value + // Note: Validation functions will update *pfinfo *only* if + // a file copy parameter has not already done so! + if (!(*pvfp->pfnfpv)(psess, + pvfp->pszName, + pszValue, + pfinfo, + perr)) { + return FALSE; // perr already filled in + } + } + } + //** Success + return TRUE; +} /* CheckForInfVarOverrides() */ + + +/*** validateParms - Validate list of file/reference parameters + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL ValidateParms(PSESSION psess, + HGENLIST hglist, + PFILEINFO pfinfo, + PERROR perr) +{ + HGENERIC hgen; + HVARIABLE hvar; + char *pszParm; + char *pszValue; + PFILEPARM pfparm; + PVALIDATEFILEPARM pvfp; + + //** Quick out if list is empty + if (!hglist) { + return TRUE; + } + + AssertSess(psess); + AssertFinfo(pfinfo); + + //** Test parameters + for (hgen=GLFirstItem(hglist); hgen; hgen=GLNextItem(hgen)) { + pszParm = GLGetKey(hgen); + Assert(pszParm != NULL); + pfparm = GLGetValue(hgen); + AssertFparm(pfparm); + pszValue = pfparm->pszValue; + Assert(pszValue != NULL); + for (pvfp=avfpFile; pvfp->pszName; pvfp++) { + if (!_stricmp(pvfp->pszName,pszParm)) { // Found the parm + break; // Exit loop + } + } + if (pvfp->pszName) { // A standard parameter + //** Validate param + if (!(*pvfp->pfnfpv)(psess, + pszParm, + pszValue, + pfinfo, + perr)) { + return FALSE; // perr already filled in + } + } + else { // Might be a custom parameter + hvar = GetInfCustomVar(psess,pszParm,FALSE,perr); + if (!hvar) { + ErrSet(perr,pszINFERR_UNDECLARED_PARM,"%s",pszParm); + return FALSE; + } + } + } + //** Success + return TRUE; +} /* validateParms() */ + + +/*** fnfpvBOOL - Validate a BOOLEAN value + * + */ +FNFPVALIDATE(fnfpvBOOL) +{ + BOOL f; + + f = BOOLfromPSZ(pszValue,perr); + if (f == -1) { + return FALSE; // perr already filled in + } + + //** Success, no need to update pv + return TRUE; +} + + +/*** fnfpvFileAttr - Validate FAT file attributes + * + */ +FNFPVALIDATE(fnfpvFileAttr) +{ + WORD attr; + PFILEINFO pfinfo; + + pfinfo = pv; + AssertFinfo(pfinfo); + + //** Parse attr + attr = FATAttrFromPsz(pszValue,perr); + if (ErrIsError(perr)) { + return FALSE; // perr already filled in + } + + //** Set attr in fileinfo, if not already set + if (!(pfinfo->flags & fifATTR_SET)) { + pfinfo->fta.attr = attr; + pfinfo->flags |= fifATTR_SET; // Override file date + } + + return TRUE; +} /* fnfpvFileAttr() */ + + +/*** fnfpvFileDate - Validate a FAT file date + * + */ +FNFPVALIDATE(fnfpvFileDate) +{ + WORD date; + PFILEINFO pfinfo; + + pfinfo = pv; + AssertFinfo(pfinfo); + + //** Parse date + date = FATDateFromPsz(pszValue,perr); + if (ErrIsError(perr)) { + return FALSE; // perr already filled in + } + + //** Set date in fileinfo, if not already set + if (!(pfinfo->flags & fifDATE_SET)) { + pfinfo->fta.date = date; + pfinfo->flags |= fifDATE_SET; // Override file date + } + + return TRUE; +} /* fnfpvFileDate() */ + + +/*** fnfpvFileTime - Validate a FAT file time + * + */ +FNFPVALIDATE(fnfpvFileTime) +{ + WORD time; + PFILEINFO pfinfo; + + pfinfo = pv; + AssertFinfo(pfinfo); + + //** Parse time + time = FATTimeFromPsz(pszValue,perr); + if (ErrIsError(perr)) { + return FALSE; // perr already filled in + } + + //** Set date in fileinfo, if not already set + if (!(pfinfo->flags & fifTIME_SET)) { + pfinfo->fta.time = time; + pfinfo->flags |= fifTIME_SET; // Override file date + } + + return TRUE; +} /* fnfpvFileTime() */ + + +/*** fnfpvLong - Validate a 32-bit integer + * + */ +FNFPVALIDATE(fnfpvLong) +{ + char *psz; + + for (psz=pszValue; *psz && isdigit(*psz); psz++) { + ; //** Make sure entire value is digits + } + if (*psz != '\0') { + ErrSet(perr,pszINFERR_NOT_A_NUMBER,"%s%s",pszName,pszValue); + return FALSE; + } + +//BUGBUG 04-Jun-1994 bens Should we verify number fits in 32-bits? + + //** Success + return TRUE; +} /* fnfpvLong() */ + + +/*** fnfpvShort - Validate a 16-bit integer + * + */ +FNFPVALIDATE(fnfpvShort) +{ + char *psz; + long l; + + for (psz=pszValue; *psz && isdigit(*psz); psz++) { + ; //** Make sure entire value is digits + } + if (*psz != '\0') { + ErrSet(perr,pszINFERR_NOT_A_NUMBER,"%s%s",pszName,pszValue); + return FALSE; + } + + //** Make sure it is in the range of a short + l = atol(pszValue); + if ((l < -32768) || (32767 < l)) { + ErrSet(perr,pszINFERR_NOT_A_SHORT,"%s%s",pszName,pszValue); + return FALSE; + } + + //** Success + return TRUE; +} /* fnfpvShort() */ + + +/*** fnfpvString - Validate an arbitrary string + * + */ +FNFPVALIDATE(fnfpvString) +{ + //** Nothing to check + return TRUE; +} /* fnfpvString() */ + + +/*** fnfpfFileName - Format destination file name + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileName) +{ + int cb; + PFILECONTEXT pfilecon; + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + cb = strlen(pfilecon->pszFile); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,pfilecon->pszFile); + return -1; + } + strcpy(pszDst,pfilecon->pszFile); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileName() */ + + +/*** fnfpfDiskNumber - Format disk number + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfDiskNumber) +{ + char ach[10]; + int cb; + int iDisk; + PCABCONTEXT pcabcon; + PDISKCONTEXT pdiskcon; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSascon(psascon); + + if (psascon->pffp == affpCabinet) { + pcabcon = psascon->pv; + AssertCabcon(pcabcon); + iDisk = pcabcon->iDisk; + } + else if (psascon->pffp == affpDisk) { + pdiskcon = psascon->pv; + AssertDiskcon(pdiskcon); + iDisk = pdiskcon->iDisk; + } + else if (psascon->pffp == affpFile) { + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + iDisk = pfinfo->iDisk; + } + else { + //** Didn't get expected type + Assert(0); + } + + sprintf(ach,"%d",iDisk); + cb = strlen(ach); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,ach); + return -1; + } + strcpy(pszDst,ach); // Copy to output buffer + return cb; // Return size +} /* fnfpfDiskNumber() */ + + +/*** fnfpfCabinetNumber - Format cabinet number + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfCabinetNumber) +{ + char ach[10]; + int cb; + int iCabinet; + PCABCONTEXT pcabcon; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSascon(psascon); + + if (psascon->pffp == affpCabinet) { + pcabcon = psascon->pv; + AssertCabcon(pcabcon); + iCabinet = pcabcon->iCabinet; + } + else if (psascon->pffp == affpFile) { + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + iCabinet = pfinfo->iCabinet; + } + else { + //** Didn't get expected type + Assert(0); + } + + sprintf(ach,"%d",iCabinet); + cb = strlen(ach); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,ach); + return -1; + } + strcpy(pszDst,ach); // Copy to output buffer + return cb; // Return size +} /* fnfpfCabinetNumber() */ + + +/*** fnfpfFileNumber - Format file number + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileNumber) +{ + char ach[10]; + int cb; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + sprintf(ach,"%d",pfinfo->iFile); + cb = strlen(ach); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,ach); + return -1; + } + strcpy(pszDst,ach); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileNumber() */ + + +/*** fnfpfFileSize - Format file size + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileSize) +{ + char ach[10]; + int cb; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + sprintf(ach,"%ld",pfinfo->cbFile); + cb = strlen(ach); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,ach); + return -1; + } + strcpy(pszDst,ach); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileSize() */ + + +/*** fnfpfFileAttr - Format file attr + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileAttr) +{ + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** NOTE: pszFromFATAttr checks for buffer overflow + return pszFromFATAttr(pszDst,cbDst,pfinfo->fta.attr); +} /* fnfpfFileAttr() */ + + +/*** fnfpfFileDate - Format file date + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileDate) +{ + ERROR err; + BOOL fMMDDYY; + HVARIABLE hvar; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + char *pszDateFmt; + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** Get date format + AssertSess(psess); + ErrClear(&err); + hvar = VarFind(psess->hvlist,pszVAR_INF_DATE_FMT,&err); + Assert(!err.fError); // Must be defined + pszDateFmt = VarGetString(hvar); + fMMDDYY = (_stricmp(pszDateFmt,pszIDF_MMDDYY) == 0); + + //** NOTE: pszFromFATDate checks for buffer overflow + return pszFromFATDate(pszDst,cbDst,pfinfo->fta.date,fMMDDYY); +} /* fnfpfFileDate() */ + + +/*** fnfpfFileTime - Format file time + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileTime) +{ + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** NOTE: pszFromFATTime checks for buffer overflow + return pszFromFATTime(pszDst,cbDst,pfinfo->fta.time); +} /* fnfpfFileTime() */ + + +/*** fnfpfCabinetFileName - Format cabinet file name + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfCabinetFileName) +{ + int cb; + PCABCONTEXT pcabcon; + + AssertSascon(psascon); + Assert(psascon->pffp == affpCabinet); + + pcabcon = psascon->pv; + AssertCabcon(pcabcon); + + cb = strlen(pcabcon->pszCab); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,pcabcon->pszCab); + return -1; + } + strcpy(pszDst,pcabcon->pszCab); // Copy to output buffer + return cb; // Return size +} /* fnfpfCabinetFileName() */ + + +/*** fnfpfDiskLabel - Format disk label + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfDiskLabel) +{ + int cb; + PDISKCONTEXT pdiskcon; + + AssertSascon(psascon); + Assert(psascon->pffp == affpDisk); + + pdiskcon = psascon->pv; + AssertDiskcon(pdiskcon); + + cb = strlen(pdiskcon->pszDisk); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,pdiskcon->pszDisk); + return -1; + } + strcpy(pszDst,pdiskcon->pszDisk); // Copy to output buffer + return cb; // Return size +} /* fnfpfDiskLabel() */ + + +/*** fnfpfFileVerNum - Format file version *number* + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileVerNum) +{ + int cb; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSess(psess); + psess->fGetVerInfo = TRUE; // Have to get file version info + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** Check for no version number (all zeros) + if ((pfinfo->verMS == 0) && (pfinfo->verLS == 0)) { + return 0; // Nothing copied to output buffer + } + + //** Format version number + cb = sprintf(psess->achMsg,"%d.%d.%d.%d", + HIWORD(pfinfo->verMS), + LOWORD(pfinfo->verMS), + HIWORD(pfinfo->verLS), + LOWORD(pfinfo->verLS)); + + //** Check for buffer overflow + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,psess->achMsg); + return -1; + } + strcpy(pszDst,psess->achMsg); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileVerNum() */ + + +/*** fnfpfFileVerString - Format file version *string* + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileVerString) +{ + int cb; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSess(psess); + psess->fGetVerInfo = TRUE; // Have to get file version info + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** Check for undefined version string + if (pfinfo->pszVersion == NULL) { + return 0; // No version string copied + } + + //** Check length and store string in buffer + cb = strlen(pfinfo->pszVersion); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s", + pszName,pfinfo->pszVersion); + return -1; + } + strcpy(pszDst,pfinfo->pszVersion); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileVerString() */ + + +/*** fnfpfFileChecksum - Format file checksum + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileChecksum) +{ + char ach[10]; + int cb; + int cHexDigits; + ULONG csum; + ULONG mask; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + HVARIABLE hvar; + + AssertSess(psess); + psess->fGetFileChecksum = TRUE; // Have to compute file checksums + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** Get the ChecksumWidth value, and mask off any undesired bits + // NOTE: By ensuring the upper bits are zero, we effectively bound + // the width of the checksum that is printed. + // Why would we throw away this valuable info, you ask? Because + // the Windows 95 setup program runs as a Win16 application, and + // is limited to 64Kb INF files. While the US English version does + // not exceed this limit, the Far East versions have many more + // files, and they do. So, there you have it! + // + hvar = VarFind(psess->hvlist,pszVAR_CHECKSUM_WIDTH,perr); + Assert(!perr->fError); // Must be defined + cHexDigits = (int)VarGetLong(hvar); // Get desired width + csum = pfinfo->checksum; // Get full checksum value + Assert(cHexDigits >= 1); + Assert(cHexDigits <= 8); + if (cHexDigits != 8) { // Need to mask off high-order bits + mask = (1L << (cHexDigits*4)) - 1; // Construct mask + csum &= mask; // Keep desired bits + } + + //** Use hex format to minimize INF file size + sprintf(ach,"%lx", csum); + cb = strlen(ach); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszName,ach); + return -1; + } + strcpy(pszDst,ach); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileChecksum() */ + + +/*** fnfpfFileLanguage - Format file language code(s) + * + * NOTE: See FNFPFORMAT for entry/exit conditions. + */ +FNFPFORMAT(fnfpfFileLanguage) +{ + int cb; + PFILECONTEXT pfilecon; + PFILEINFO pfinfo; + + AssertSess(psess); + psess->fGetVerInfo = TRUE; // Only for checkInfLineFormats() + + AssertSascon(psascon); + Assert(psascon->pffp == affpFile); + + pfilecon = psascon->pv; + AssertFilecon(pfilecon); + + pfinfo = pfilecon->pfinfo; + AssertFinfo(pfinfo); + + //** Check for undefined string + if (pfinfo->pszLang == NULL) { + return 0; // No string copied + } + + //** Check length and store string in buffer + cb = strlen(pfinfo->pszLang); + if (cb >= cbDst) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s", + pszName,pfinfo->pszLang); + return -1; + } + strcpy(pszDst,pfinfo->pszLang); // Copy to output buffer + return cb; // Return size +} /* fnfpfFileLanguage() */ + + +/*** checkInfLineFormats - Check validity of InfXxxLineFormat variables + * + * We do this by crofting up fake data for one disk, one cabinet, and one + * file line, and trying to format each of these with the the same code that + * does it at INF generation time. If these all succeed, we must be OK! + * + * We also have code in each of the ver info formatting routines to set the + * psess->fGetVerInfo flag, so we can tell that we have to spend the extra + * time extracting version information from every file! + * + * We also have code in the file checksum formatting routine to set the + * psess->fGetFileChecksum flag, so we can tell that we have to spend the + * extra time computing source file checksums for every file! + * + * Entry: + * psess - Session + * perr - ERROR + * + * Exit-Success: + * Returns TRUE; formats are OK. + * psess->fGetVerInfo set if any of the parameters *ver*, *vers*, or + * *lang* were referenced. + * psess->fGetFileChecksum set if *csum* parameter is referenced. + * + * Exit-Failure: + * Returns FALSE; perr filled in + */ +BOOL checkInfLineFormats(PSESSION psess,PERROR perr) +{ + CABCONTEXT cabcon; + DISKCONTEXT diskcon; + FILECONTEXT filecon; + HVARIABLE hvar; + PINF pinf; + FILEINFO finfo; + char *pszFmt; + SASCONTEXT sascon; + + AssertSess(psess); + pinf = PINFfromHINF(psess->hinf); + AssertInf(pinf); + + SetAssertSignature((&sascon),sigSASCONTEXT); + sascon.psess = psess; + sascon.hglistParm = NULL; + +/* + ** Check InfDiskLineFormat + */ + //** Get disk line format + hvar = VarFind(psess->hvlist,pszVAR_INF_DISK_LINE_FMT,perr); + Assert(!perr->fError); // Must be defined + pszFmt = VarGetString(hvar); // Disk line format + + //** Format and write line to file + SetAssertSignature((&diskcon),sigDISKCONTEXT); + diskcon.iDisk = 1; + diskcon.pszDisk = "test"; + + sascon.pffp = affpDisk; + sascon.pv = &diskcon; + + if (!SubstituteFormatString(pinf->achLine, + sizeof(pinf->achLine), + pszFmt, + fnfpInfLine, + &sascon, + perr)) { + strcpy(pinf->achLine,perr->ach); // Save details + ErrSet(perr,pszINFERR_BAD_FMT,"%s%s", + pszVAR_INF_DISK_LINE_FMT, pinf->achLine); + return FALSE; + } + +/* + ** Check InfCabinetLineFormat + */ + //** Get cabinet line format + hvar = VarFind(psess->hvlist,pszVAR_INF_CAB_LINE_FMT,perr); + Assert(!perr->fError); // Must be defined + pszFmt = VarGetString(hvar); // Cabinet line format + + //** Format and write line to file + SetAssertSignature((&cabcon),sigCABCONTEXT); + cabcon.iCabinet = 1; + cabcon.iDisk = 1; + cabcon.pszCab = "test"; + + sascon.pffp = affpCabinet; + sascon.pv = &cabcon; + + if (!SubstituteFormatString(pinf->achLine, + sizeof(pinf->achLine), + pszFmt, + fnfpInfLine, + &sascon, + perr)) { + strcpy(pinf->achLine,perr->ach); // Save details + ErrSet(perr,pszINFERR_BAD_FMT,"%s%s", + pszVAR_INF_CAB_LINE_FMT, pinf->achLine); + return FALSE; + } + +/* + ** Check InfFileLineFormat + */ + //** Fill in dummy FILEINFO structure + SetAssertSignature((&finfo),sigFILEINFO); + finfo.pszDDF = "test"; + finfo.ilineDDF = 1; + finfo.cbFile = 1; + finfo.iDisk = 1; + finfo.iCabinet = 1; + finfo.fta.date = 0; + finfo.fta.time = 0; + finfo.fta.attr = 0; + finfo.hglistInfLines = NULL; + finfo.flags = 0; + finfo.hglistParm = NULL; + finfo.checksum = 0; + finfo.verMS = 0; + finfo.verLS = 0; + finfo.pszVersion = NULL; + finfo.pszLang = NULL; + + //** Get line format + hvar = VarFind(psess->hvlist,pszVAR_INF_FILE_LINE_FMT,perr); + Assert(!perr->fError); // Must be defined + pszFmt = VarGetString(hvar); // File line format + + //** Format and write line to file + SetAssertSignature((&filecon),sigFILECONTEXT); + filecon.pszFile = "a test"; + filecon.pfinfo = &finfo; + + sascon.pffp = affpFile; + sascon.pv = &filecon; + + if (!SubstituteFormatString(pinf->achLine, + sizeof(pinf->achLine), + pszFmt, + fnfpInfLine, + &sascon, + perr)) { + strcpy(pinf->achLine,perr->ach); // Save details + ErrSet(perr,pszINFERR_BAD_FMT,"%s%s", + pszVAR_INF_FILE_LINE_FMT, pinf->achLine); + return FALSE; + } + +/* + ** SUCCESS + */ + return TRUE; +} /* checkInfLineFormats() */ + + +/*** fnfpInfLine - Format parameter in an INF File, Disk, or Cab line + * + * NOTE: See format.h for entry/exit conditions. + * + * Strategy: + * Determine where to get value from, according to this priority: + * (1) Parameter list + * (2) InfXxxx variable + * (3) Standard parameter + * + * Error checking: + * As this is called from pass 2, we can rely upon pass 1 to catch + * certain errors (like /x=y on a file copy/reference command without + * the variable InfX being defined). We will wait and catch truly + * undefined parameters in the InfXxxLineFormat variables until now, + * because it is easier than writing another routine to scan through + * and look for undefined parms. + */ +FNFORMATPARM(fnfpInfLine) +{ + char achDate[20]; + int cb; + WORD date; + BOOL fMMDDYY; // TRUE => use MM/DD/YY format + HVARIABLE hvar; + PSASCONTEXT psascon; + PFORMATFILEPARM pffp=NULL; + PFILEPARM pfparm; + PSESSION psess; + char *pszDateFmt; + char *pszValue; + + psascon = pv; + AssertSascon(psascon); + psess = psascon->psess; + AssertSess(psess); + + //** (1) Look on parm list + pfparm = GLFindAndGetValue(psascon->hglistParm,pszParm); + + //** (2) Look for InfXxxxx variable (copy to pass 2 list if pass 1) + hvar = GetInfCustomVar(psess,pszParm,TRUE,perr); + + //** (3) Look for a standard parameter if we didn't find a variable + if (!hvar) { + for (pffp=psascon->pffp; pffp->pszName; pffp++) { + if (!_stricmp(pffp->pszName,pszParm)) { // Found the parm + break; // Exit loop + } + } + if (!pffp->pszName) { // No match + pffp = NULL; // Indicate failure for below + } + } + +/* + * OK, now we have the following state: + * pfparm - non-null if value is on parm list + * hvar - non-null if InfXxxx variable is defined for parm + * pffp - non-null if hvar==NULL *and* standard parm + */ + + if (pfparm || hvar) { // Use parm list or variable value + if (pfparm && !(hvar || pffp)) { + //** Pass 1 should catch /x=y without InfX variable definition! + Assert(0); + return -1; + } + else if (pfparm) { // Use parm list value + AssertFparm(pfparm); + pszValue = pfparm->pszValue; + } + else { + Assert(hvar != NULL); // Use variable value + pszValue = VarGetString(hvar); + Assert(pszValue != NULL); + } + + //** See if we have to normalize date format + if (!_stricmp(pszParm,pszPARM_FILEDATE)) { + date = FATDateFromPsz(pszValue,perr); // Convert to date + Assert(!perr->fError); // Was already validated + hvar = VarFind(psess->hvlist,pszVAR_INF_DATE_FMT,perr); + Assert(!perr->fError); // Must be defined + pszDateFmt = VarGetString(hvar); + fMMDDYY = (_stricmp(pszDateFmt,pszIDF_MMDDYY) == 0); + if (-1 == pszFromFATDate(achDate,sizeof(achDate),date,fMMDDYY)) { + return -1; + } + pszValue = achDate; // Use reformated date string + } + + //** copy value + cb = strlen(pszValue); + if (cb >= cbOut) { + ErrSet(perr,pszINFERR_BUFFER_OVERFLOW,"%s%s",pszParm,pszValue); + return -1; + } + strcpy(pszOut,pszValue); + return cb; + } + else if (pffp) { + //** Format a standard parameter + return (*pffp->pfnfpf)(psascon->psess, // Session + pszOut, // Output buffer + cbOut, // Output buffer size + pszParm, // Parameter name + psascon, // SAS Context + perr); + } + else { + //** Parameter name was never defined at all! No InfX variable, + // no /X on a file copy or reference command, and it is not a + // standard parameter. + ErrSet(perr,pszINFERR_UNDEFINED_PARM,"%s",pszParm); + return -1; + } + Assert(0); +} /* fnfpInfLine() */ + + +/*** pszFromFATDate - Convert MS-DOS file date to string + * + * Entry: + * pszDst - Buffer to receive formatted date + * cbDst - Length of psz buffer + * date - MS-DOS FAT file system date format (see below) + * fMMDDYY - TRUE => use mm/dd/yy; FALSE => use yyyy-mm-dd + * + * Exit-Success: + * Returns length of string stored in *psz (not counting NUL byte) + * + * Exit-Failure: + * Returns -1; buffer too small + * + * NOTE: This is the interpretation of the MS-DOS date value: + * + * Date Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Day (1 - 31) + * 5 - 8 4 Month (1 - 12) + * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14) + */ +int pszFromFATDate(char *pszDst, int cbDst, WORD date, BOOL fMMDDYY) +{ + char ach[50]; + int cb; + int day; + int month; + int year; + + day = (date & 0x1f); + month = (date >> 5) & 0x0f; + year = ((date >> 9) & 0x7f) + yearFAT_DATE_MIN; + + if (fMMDDYY) { + //** Adjust year to two digits, if appropriate + if (year < 2000) { // 1980..1999 -> 80..99 + year -= 1900; + } + else if (year < 2080) { // 2000..2079 -> 00..79 + year -= 2000; + } + //** Format mm/dd/yy + MsgSet(ach,pszINF_DATE_FMT,"%02d%02d%02d",month,day,year); + } + else { + //** Format yyyy-mm-dd + MsgSet(ach,pszINF_DATE_FMT2,"%04d%02d%02d",year,month,day); + } + + cb = strlen(ach); + if (cb >= cbDst) { + return -1; // Buffer too small + } + strcpy(pszDst,ach); + return cb; +} /* pszFromFATDate() */ + + +/*** FATDateFromPsz - Convert string to MS-DOS file date + * + * Entry: + * pszDate - String with MM/DD/YY or YYYY-MM-DD date + * perr - ERROR structure + * + * Exit-Success: + * Returns FAT file system date; + * perr is clear (ErrIsError(perr) is FALSE). + * + * Exit-Failure: + * Returns 0; perr is filled with an error + * + * NOTE: This is the interpretation of the MS-DOS date value: + * + * Date Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Day (1 - 31) + * 5 - 8 4 Month (1 - 12) + * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14) + * (1980..2107) + */ +WORD FATDateFromPsz(char *pszDate, PERROR perr) +{ + char chSep; + WORD date; + int day; + int month; + char *psz; + int t; + int year; + + //** Make sure date is long enough to include first separator + if (strlen(pszDate) < 5) { // Check for minimum length + ErrSet(perr,pszINFERR_INVALID_DATE_FORMAT,"%s",pszDate); + return 0; + } + + //** Choose between two date formats + if (pszDate[2] == chINF_DATE_SEP) { + chSep = pszDate[2]; // Remember separator ('/') + } + else if (pszDate[4] == chINF_DATE_SEP2) { + chSep = pszDate[4]; // Remember separator ('-') + } + else { + ErrSet(perr,pszINFERR_INVALID_DATE_FORMAT,"%s",pszDate); + return 0; + } + + //** Parse off month (or year) + month = atoi(pszDate); + if (!(psz = strchr(pszDate,chSep))) { + ErrSet(perr,pszINFERR_MISSING_DATE_SEPARATOR,"%c%s",chSep,pszDate); + return 0; + } + psz++; // Skip over separator + + //** Parse off day (or month) + day = atoi(psz); + if (!(psz = strchr(psz,chSep))) { + ErrSet(perr,pszINFERR_MISSING_DATE_SEPARATOR,"%c%s",chSep,pszDate); + return 0; + } + psz++; // Skip over separator + + //** Parse off year (or day) + year = atoi(psz); + + //** Shuffle around numbers if YYYY-MM-DD format + if (chSep == chINF_DATE_SEP2) { + t = day; // save month + day = year; + year = month; + month = t; + } + + //** Check ranges (but not invalid dates!) + if ((day < 1) || (day > 31) || ((month == 2) && (day > 29))) { + ErrSet(perr,pszINFERR_BAD_DAY_IN_DATE,"%d%s",day,pszDate); + return 0; + } + if ((month < 1) || (month > 12)) { + ErrSet(perr,pszINFERR_BAD_MONTH_IN_DATE,"%d%s",day,pszDate); + return 0; + } + + //** Permit both two digit (94) and 4 digit (1994) dates + if (year < 100) { // Convert to 4-digit date + if (year < 80) { // Must be 20xx + year += 2000; + } + else { // Must be 19xx + year += 1900; + } + } + + if (year < yearFAT_DATE_MIN) { + ErrSet(perr,pszINFERR_LO_YEAR_IN_DATE,"%d%d%s", + year,yearFAT_DATE_MIN,pszDate); + return 0; + } + if (year > yearFAT_DATE_MAX) { + ErrSet(perr,pszINFERR_HI_YEAR_IN_DATE,"%d%d%s", + year,yearFAT_DATE_MAX,pszDate); + return 0; + } + + //** Combine year, month, and day + date = (year-1980)<<9 | month<<5 | day; + ErrClear(perr); // Make sure error is clear + return date; +} /* FATDateFromPsz() */ + + +/*** pszFromFATTime - Convert MS-DOS file time to string + * + * Entry: + * pszDst - Buffer to receive formatted time + * cbDst - Length of psz buffer + * time - MS-DOS FAT file system time format (see below) + * + * Exit-Success: + * Returns length of string stored in *psz (not counting NUL byte) + * + * Exit-Failure: + * Returns -1; buffer too small + * + * NOTE: This is the interpretation of the MS-DOS time value: + * + * Time Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Number of two-second increments (0 - 29) + * 5 - 10 6 Minutes (0 - 59) + * 11 - 15 5 Hours (0 - 23) + */ +int pszFromFATTime(char *pszDst, int cbDst, WORD time) +{ + char ach[50]; + int cb; + int sec; + int min; + int hour; + char chAMPM; // AM/PM string + + sec = (time & 0x1f) << 1; + min = (time >> 5) & 0x3f; + hour = (time >> 11) & 0x1f; + + //** Get am/pm extension, and map 0 to 12 + if (hour >= 12) { + chAMPM = chINF_TIME_PM; + hour -= 12; + } + else { + chAMPM = chINF_TIME_AM; + } + if (hour == 0) { + hour = 12; + } + + MsgSet(ach,pszINF_TIME_FMT,"%02d%02d%02d%c",hour,min,sec,chAMPM); + cb = strlen(ach); + if (cb >= cbDst) { + return -1; // Buffer too small + } + strcpy(pszDst,ach); + return cb; +} /* pszFromFATTime() */ + + +/*** FATTimeFromPsz - Convert string to MS-DOS file date + * + * Entry: + * pszTime - String with HH:MM:SSa|p format + * perr - ERROR structure + * + * Exit-Success: + * Returns FAT file system time; + * perr is clear (ErrIsError(perr) is FALSE). + * + * Exit-Failure: + * Returns 0; perr is filled with an error + * + * NOTE: This is the interpretation of the MS-DOS time value: + * + * Time Bits cBits Meaning + * --------- ----- ---------------------------------------- + * 0 - 4 5 Number of two-second increments (0 - 29) + * 5 - 10 6 Minutes (0 - 59) + * 11 - 15 5 Hours (0 - 23) + */ +WORD FATTimeFromPsz(char *pszTime, PERROR perr) +{ + char ch; + int hour; + int min; + char *psz; + int sec; + WORD time; + + //** Parse off hour + hour = atoi(pszTime); + if (!(psz = strchr(pszTime,chINF_TIME_SEP))) { + ErrSet(perr,pszINFERR_MISSING_TIME_SEPARATOR,"%c%s", + chINF_TIME_SEP,pszTime); + return 0; + } + psz++; // Skip over separator + + //** Parse off minute + min = atoi(psz); + if (!(psz = strchr(psz,chINF_TIME_SEP))) { + ErrSet(perr,pszINFERR_MISSING_TIME_SEPARATOR,"%c%s", + chINF_TIME_SEP,pszTime); + return 0; + } + psz++; // Skip over separator + + //** Parse off seconds + sec = atoi(psz); + + //** Parse off a/p + ch = tolower(psz[2]); // Require 2 digits for seconds + switch (ch) { + case chINF_TIME_AM: + case chINF_TIME_PM: + //** Check range + if ((hour < 1) || (hour > 12)) { + ErrSet(perr,pszINFERR_BAD_HOUR_IN_TIME,"%d%s",hour,pszTime); + return 0; + } + //** Convert to 24-hour clock + //** 12:xx AM -> 0 + //** 1:xx AM -> 1 + //** ... + //** 11:xx AM -> 11 + //** 12:xx PM -> 12 + //** 1:xx PM -> 13 + //** ... + //** 11:xx PM -> 23 + + if (hour == 12) { // Move 12 back to 0 + hour = 0; + } + if (ch == chINF_TIME_PM) { + hour += 12; // Swing PM times up + } + break; + + case '\0': // Assume 24-hour clock + break; + + default: + ErrSet(perr,pszINFERR_BAD_TIME_AM_PM,"%c%s",ch,pszTime); + return 0; + } + + //** Check ranges + if ((sec < 0) || (sec > 59)) { + ErrSet(perr,pszINFERR_BAD_SEC_IN_TIME,"%d%s",sec,pszTime); + return 0; + } + if (sec % 2) { // Only permit even seconds + ErrSet(perr,pszINFERR_ODD_SEC_IN_TIME,"%d%s",sec,pszTime); + return 0; + } + if ((min < 0) || (min > 59)) { + ErrSet(perr,pszINFERR_BAD_MINUTE_IN_TIME,"%d%s",min,pszTime); + return 0; + } + if ((hour < 0) || (hour > 23)) { + ErrSet(perr,pszINFERR_BAD_HOUR_IN_TIME,"%d%s",hour,pszTime); + return 0; + } + + //** Combine hour, minute, and second + time = hour<<11 | min<<5 | sec>>1; + ErrClear(perr); // Make sure error is clear + return time; +} /* FATTimeFromPsz() */ + + +/*** pszFromFATAttr - Convert MS-DOS file date to string + * + * Entry: + * pszDst - Buffer to receive formatted date + * cbDst - Length of psz buffer + * date - MS-DOS FAT file system date format (see below) + * + * Exit-Success: + * Returns length of string stored in *psz (not counting NUL byte) + * + * Exit-Failure: + * Returns -1; buffer too small + */ +int pszFromFATAttr(char *pszDst, int cbDst, WORD attr) +{ + char *psz; + + Assert(pszDst != NULL); + psz = pszDst; + + //** Require worst-case buffer size + if (cbDst < 5) { + return -1; + } + + //** Create string representation + if (attr & _A_ARCH ) *psz++ = chINF_ATTR_ARCHIVE; + if (attr & _A_HIDDEN) *psz++ = chINF_ATTR_HIDDEN; + if (attr & _A_RDONLY) *psz++ = chINF_ATTR_READONLY; + if (attr & _A_SYSTEM) *psz++ = chINF_ATTR_SYSTEM; + + //** Terminate string + *psz = '\0'; + + //** Return length (not counting NUL byte) + return psz-pszDst; +} /* pszFromFATAttr() */ + + +/*** FATAttrFromPsz - Convert string to MS-DOS file attributes + * + * Entry: + * pszAttr - String composed of letters from {R,H,S,A} + * perr - ERROR structure + * + * Exit-Success: + * Returns FAT file system attributes; + * perr is clear (ErrIsError(perr) is FALSE). + * + * Exit-Failure: + * Returns 0; perr is filled with an error + */ +WORD FATAttrFromPsz(char *pszAttr, PERROR perr) +{ + WORD attr; + char ch; + char *psz; + + Assert(pszAttr != NULL); + attr = 0; // No attributes, yet + for (psz=pszAttr; *psz; psz++) { + ch = toupper(*psz); + switch (ch) { + case chINF_ATTR_ARCHIVE: attr |= _A_ARCH; break; + case chINF_ATTR_HIDDEN: attr |= _A_HIDDEN; break; + case chINF_ATTR_READONLY: attr |= _A_RDONLY; break; + case chINF_ATTR_SYSTEM: attr |= _A_SYSTEM; break; + + default: + ErrSet(perr,pszERRINF_BAD_ATTR,"%c%s",*psz,pszAttr); + return 0; + } + } + + //** Success + return attr; +} /* FATAttrFromPsz() */ + + +/*** GetInfCustomVar - See if InfXxxx variable exists + * + * Entry: + * psess - Session + * pszParm - Parameter name (suffix for Inf) + * fCopy - TRUE => Copy to pass 2 list if exists and this is pass 1 + * perr - ERROR structure + * + * Exit-Success: + * Returns HVARIABLE; + * + * Exit-Failure: + * Returns NULL; variable not found *or* error occured (use + * ErrIsError(perr) to determine what happened). + */ +HVARIABLE GetInfCustomVar(PSESSION psess,char *pszParm,BOOL fCopy,PERROR perr) +{ + char achVar[cbVAR_NAME_MAX]; + ERROR errIgnore; + HVARIABLE hvar; + char *pszValue; + + //** Construct InfXxxx variable name + Assert(sizeof(achVar) > strlen(pszPREFIX_INF_VARS)); + strcpy(achVar,pszPREFIX_INF_VARS); // Get prefix + if (strlen(pszParm)+strlen(achVar) >= sizeof(achVar)) { + ErrSet(perr,pszINFERR_PARM_NAME_TOO_LONG,"%d%s", + sizeof(achVar)-1,pszParm); + return NULL; + } + strcat(achVar,pszParm); // Append parameter name + + //** Discard error message from VarFind + hvar = VarFind(psess->hvlist,achVar,&errIgnore); + + //** If this is pass 1, and we are checking the InfXxxxLineFormat + // variables, so we need to carry over this variable to the pass 2 + // variable list so that it will be available when the Disk and Cabinet + // lines get generated. For *relational-mode* DDFs, this allows the + // DDF author to put *all* INF-related variables in the *INF* area -- + // after all of the file layout! + // + if (fCopy && hvar && !psess->fPass2) { + Assert(psess->hvlistPass2 != NULL); // Must have been cloned already + pszValue = VarGetString(hvar); // Get value + if (!VarSet(psess->hvlistPass2,achVar,pszValue,perr)) { + return NULL; // Error + } + } + + //** Return result + return hvar; +} /* GetInfCustomVar() */ diff --git a/private/windows/diamond/inf.h b/private/windows/diamond/inf.h new file mode 100644 index 000000000..95700303c --- /dev/null +++ b/private/windows/diamond/inf.h @@ -0,0 +1,304 @@ +/*** inf.h - Diamond INF generation definitions + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 23-Feb-1994 bens Initial version + * 24-Feb-1994 bens Remove PSESSION dependency + * 02-Mar-1994 bens Add function header comments + */ + +#ifndef INCLUDED_INF +#define INCLUDED_INF 1 + +#include "types.h" +#include "error.h" +#include "glist.h" +#include "dfparse.h" + +#define cbINF_LINE_MAX 512 // Maximum INF line length + +#define cbPARM_NAME_MAX 32 // Maximum parameter name length + + +#ifdef ASSERT +#define sigLINEINFO MAKESIG('L','I','N','E') // LINEINFO signature +#define AssertLinfo(p) AssertStructure(p,sigLINEINFO); +#else // !ASSERT +#define AssertLinfo(p) +#endif // !ASSERT + +/*** LINEINFO - Info for a line + * + * This is stored in a GENLIST with a NULL key. + */ +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigLINEINFO +#endif + INFAREA inf; // Area of INF to write line to + char *pszLine; // Line to write to INF +} LINEINFO; /* linfo */ +typedef LINEINFO *PLINEINFO; /* plinfo */ + + +#ifdef ASSERT +#define sigFILEINFO MAKESIG('F','I','N','F') // FILEINFO signature +#define AssertFinfo(p) AssertStructure(p,sigFILEINFO); +#else // !ASSERT +#define AssertFinfo(p) +#endif // !ASSERT + +/*** fifXXX - flags for FILEINFO.flags field + * + */ +#define fifWRITTEN_TO_INF 0x0001 // File has been written to INF +#define fifREFERENCED 0x0002 // File has been referenced in INF section +#define fifDATE_SET 0x0004 // Explicit file date has been set in .fta +#define fifTIME_SET 0x0008 // Explicit file date has been set in .fta +#define fifATTR_SET 0x0010 // Explicit file date has been set in .fta + + +/*** FILEINFO - Info associated with a file + * + * The destination file name is used as the key in a GENLIST, and a + * pointer to this structure is stored in the user pointer in the GENLIST + * item. + */ +typedef struct { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigFILEINFO +#endif + long cbFile; // File size + char *pszDDF; // DDF file name with file copy command + int ilineDDF; // Line in DDF of file copy command + int iDisk; // Disk number (1-based) + int iCabinet; // Cabinet number (1-based, 0=> not in cab) + int iFile; // File number (1-based) + int flags; // fifXxxx flags + FILETIMEATTR fta; // date/time/attributes + HGENLIST hglistParm; // Parameter list + HGENLIST hglistInfLines; // Lines to add to INF after this file + ULONG checksum; // 32-bit checksum of file contents + ULONG verMS; // Most significant 32-bits of file ver + // (0 if no value). + + ULONG verLS; // Least significant 32-bits of file ver + // (0 if no value). + + char *pszVersion; // String version via VER.DLL (NULL if no + // value). + // NOTE: Can be different from verMS/LS, + // due to sloppy build procedures! + + char *pszLang; // Language via VER.DLL API (NULL if no + // value) +} FILEINFO; /* finfo */ +typedef FILEINFO *PFILEINFO; /* pfinfo */ + +#define idiskBAD -1 // Unitialized value for finfo.iDisk +#define icabBAD -1 // Unitialized value for finfo.iCabinet + + +/*** HINF - handle to INF context + * + */ +typedef void *HINF; /* hinf */ + + +/*** DestroyFileInfo - Destroy a file info structure + * + * Entry: + * pv - pointer to a FILEINFO (may be NULL, which is a NOP) + * + * Exit: + * FILEINFO destroyed; + */ +FNGLDESTROYVALUE(DestroyFileInfo); + + +/*** DestroyLineInfo - Destroy a line info structure + * + * Entry: + * pv - pointer to a LINEINFO (may be NULL, which is a NOP) + * + * Exit: + * LINEINFO destroyed; + */ +FNGLDESTROYVALUE(DestroyLineInfo); + + +/*** infCreate - Create INF context + * + * Entry: + * psess - SESSION structure + * perr - ERROR structure + * + * Exit-Success: + * returns handle to INF context + * + * Exit-Failure: + * Returns NULL; perr filled in with error. + */ +HINF infCreate(PSESSION psess, PERROR perr); + + +/*** infAddLine - Add a line to an INF area + * + * Entry: + * hinf - handle to INF context + * inf - INF area + * pszLine - Line to add + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; line added to INF + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL infAddLine(HINF hinf, INFAREA inf, char *pszLine, PERROR perr); + + +/*** infAddCabinet - Add information for a new cabinet to the INF context + * + * Entry: + * psess - Session + * iCabinet - cabinet number being added + * iDisk - disk number where cabinet resides + * pszCab - cabinet file name + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; cabinet info added to INF context + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL infAddCabinet(PSESSION psess, + int iCabinet, + int iDisk, + char *pszCab, + PERROR perr); + + +/*** infAddDisk - Add information for a new disk to the INF context + * + * Entry: + * psess - Session + * iDisk - disk number begin added + * pszDisk - printed label for disk + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; disk info added to INF context + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL infAddDisk(PSESSION psess, int iDisk, char *pszDisk, PERROR perr); + + +/*** infAddFile - Add information for a new file to the INF context + * + * Entry: + * psess - Session + * pszFile - Destination file name + * pfinfo - File information + * hglistParm - Parameter list + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; cabinet info added to INF context + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL infAddFile(PSESSION psess, + char *pszFile, + PFILEINFO pfinfo, + HGENLIST hglistParm, + PERROR perr); + + +/*** infGenerate - Generate INF file + * + * NOTE: See inf.h for entry/exit conditions. + */ +BOOL infGenerate(HINF hinf, + char *pszInfFile, + time_t *ptime, + char *pszVer, + PERROR perr); + + +/*** infDestroy - Destroy INF context + * + * Entry: + * hinf - handle to INF context to destroy + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; INF context destroyed. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL infDestroy(HINF hinf, PERROR perr); + + +/*** ValidateParms - Validate list of file parameters + * + * Entry: + * psess - Session + * hglist - List of FILEPARMs + * pfinfo - Fileinfo structure to modify (if appropriate) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; All parameters are valid, *pfinfo modified if + * appropriate (for date, time, attr, size, ...) + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * NOTE: This must be called *before* CheckForInfVarOverrides(), so that + * any parms specified on the file copy command have precedence + * over the values of InfDate, InfTime, and InfAttr variables! + */ +BOOL ValidateParms(PSESSION psess, + HGENLIST hglist, + PFILEINFO pfinfo, + PERROR perr); + + +/*** CheckForInfVarOverrides - Apply any InfXxxx overrides to FILEINFO + * + * Entry: + * psess - Session + * pfinfo - Fileinfo structure to modify (if indicated) + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; All parameters are valid, *pfinfo modified if + * appropriate (for date, time, attr) *only* if pfinfo->flags + * indicates the values have not yet been set. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + * + * NOTE: This must be called *after* ValidateParms(), so that + * it will not override parms specified on the file copy + * command! + */ +BOOL CheckForInfVarOverrides(PSESSION psess, + PFILEINFO pfinfo, + PERROR perr); + + +#endif // !INCLUDED_INF diff --git a/private/windows/diamond/inf.msg b/private/windows/diamond/inf.msg new file mode 100644 index 000000000..61fec32f9 --- /dev/null +++ b/private/windows/diamond/inf.msg @@ -0,0 +1,86 @@ +/*** inf.msg - DIAMOND.EXE INF File displayable strings + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * History: + * 23-Feb-1994 bens Initial version + * 24-Feb-1994 bens Fleshed out + * 01-Mar-1994 bens Added header lines + * 02-May-1994 bens Added custom INF stuff + * 09-May-1994 bens Added YYYY-MM-DD date format + * 04-Jun-1994 bens Support ver parameters + * 06-Jul-1994 msliger fixed continuation line error + * 12-Jul-1994 bens Moved INF header/footer strings to dfparse.msg + */ + + +//** Tempfile name prefix + +#define pszINF_TMP_PREFIX "dia" + + +//** Component strings + +#define pszINF_TMP_FILED "INF disks temporary file" +#define pszINF_TMP_FILEC "INF cabinets temporary file" +#define pszINF_TMP_FILEF "INF files temporary file" + +#define pszINF_HEADER_TEMPLATE "INF header variable template" + + +//** Formatting strings + +#define pszINF_DATE_FMT "%1/%2/%3" // mm/dd/yy +#define pszINF_DATE_FMT2 "%1-%2-%3" // yyyy-mm-dd + +#define pszINF_TIME_FMT "%1:%2:%3%4" +#define chINF_TIME_AM 'a' +#define chINF_TIME_PM 'p' + +#define chINF_DATE_SEP '/' // mm/dd/yy +#define chINF_DATE_SEP2 '-' // yyyy-mm-dd +#define chINF_TIME_SEP ':' + +#define chINF_ATTR_ARCHIVE 'A' +#define chINF_ATTR_HIDDEN 'H' +#define chINF_ATTR_READONLY 'R' +#define chINF_ATTR_SYSTEM 'S' + + +//** Error strings + +#define pszINFERR_CANT_CREATE_INF "Cannot create INF file: %1" +#define pszINFERR_OUT_OF_MEMORY "Out of memory creating INF file" +#define pszINFERR_NO_MEM_CATING_FILE "Out of memory copying %1 %2 to %3" +#define pszINFERR_READING "Read failed copying %1 %2 to %3" +#define pszINFERR_WRITING "Write failed copying %1 %2 to %3" +#define pszINFERR_INF_WRITE "Write of %1 failed on INF file: %2" + +#define pszINFERR_CANNOT_CHANGE_PARM "Cannot change parameter: %1" +#define pszINFERR_BUFFER_OVERFLOW "Buffer overflow expanding '%1' parameter value: %2" +#define pszINFERR_UNDECLARED_PARM "Undeclared parameter: /%1" +#define pszINFERR_UNDEFINED_PARM "Undefined parameter: %1" +#define pszINFERR_PARM_NAME_TOO_LONG "Parameter name exceeds maximum length(%1): %2" + +#define pszINFERR_NOT_A_NUMBER "Value of paramter '%1' must be a number: %2" +#define pszINFERR_NOT_A_SHORT "Value of paramter '%1' must be between -32768 and 32767: %2" + +#define pszINFERR_INVALID_DATE_FORMAT "Bad date format: %1" +#define pszINFERR_MISSING_DATE_SEPARATOR "Missing separator (%1) in date: %2" +#define pszINFERR_BAD_DAY_IN_DATE "Bad day (%1) in date: %2" +#define pszINFERR_BAD_MONTH_IN_DATE "Bad month (%1) in date: %2" +#define pszINFERR_LO_YEAR_IN_DATE "Year (%1) lower than minimum (%2) in date: %3" +#define pszINFERR_HI_YEAR_IN_DATE "Year (%1) higher than maximum (%2) in date: %3" + +#define pszINFERR_MISSING_TIME_SEPARATOR "Missing separator (%1) in time: %2" +#define pszINFERR_BAD_SEC_IN_TIME "Bad second (%1) in time: %2" +#define pszINFERR_ODD_SEC_IN_TIME "Odd second (%1) in time: %2" +#define pszINFERR_BAD_MINUTE_IN_TIME "Bad minute (%1) in time: %2" +#define pszINFERR_BAD_HOUR_IN_TIME "Bad hour (%1) in time: %2" +#define pszINFERR_BAD_TIME_AM_PM "Bad AM/PM character (%1) in time: %2" + +#define pszERRINF_BAD_ATTR "Bad character (%1) in file attribute: %2" + +#define pszINFERR_BAD_FMT "Bad format in %1: %2" diff --git a/private/windows/diamond/layout.c b/private/windows/diamond/layout.c new file mode 100644 index 000000000..b1902b647 --- /dev/null +++ b/private/windows/diamond/layout.c @@ -0,0 +1,53 @@ +/*** layout.c - Layout Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 20-Aug-1993 bens Initial version + * + * Algorithm: + * fFillingCabinet = FALSE + * groupHistory = EMPTY + * WHILE another file still to place DO + * IF OnDisk list for this disk is not empty THEN + * Remove file from the current OnDisk list + * Place file on current disk + * IF file overflowed to a new disk THEN + * ERROR - Could not place OnDisk file on desired disk + * ENDIF + * ELSE IF fFillingDisk THEN + * Remove file from the Filler list + * Place file on current disk + * IF file overflowed to a new disk THEN + * fFillingCabinet = FALSE + * ENDIF + * ELSE + * Remove file from Normal list + * IF file is the start of a .Group THEN + * Add file to groupHistory + * ENDIF + * Place file on current disk + * IF file overflowed to a new disk AND file is in a .GROUP THEN + * IF Filler list is empty THEN + * WARNING - unable to keep group on a single disk + * ELSE + * Remove all files in groupHistory from disk + * Move all files in groupHistory back onto Normal list + * fFillingCabinet = TRUE + * ENDIF + * ENDIF + * ENDIF + * ENDWHILE + */ + +//BUGBUG 07-Feb-1994 bens Some lines of code so LOC won't choke + +int doTheLayoutNow(void) +{ + return 0; +} diff --git a/private/windows/diamond/mem.c b/private/windows/diamond/mem.c new file mode 100644 index 000000000..bad8f02bf --- /dev/null +++ b/private/windows/diamond/mem.c @@ -0,0 +1,242 @@ +/*** mem.c - Memory Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Lift code from STOCK.EXE win app + * 12-Aug-1993 bens Get strings from memory.msg + * 01-Sep-1993 bens Add NULL pointer checks to MMAssert and MMStrDup + * 18-Mar-1994 bens Make sure non-assert build works; rename + * 18-May-1994 bens Allow turning off MemCheckHeap() in debug build + * (it can really, really slow things down!) + * + * Functions: + * MemAlloc - Allocate memory block + * MemFree - Free memory block + * MemStrDup - Duplicate string to new memory block + * + * Functions available in ASSERT build: + * MemAssert - Assert that pointer was allocated by MemAlloc + * MemCheckHeap - Check entire memory heap + * MemGetSize - Return allocated size of memory block + * MemSetCheckHeap - Control whether MemCheckHeap is done on every + * every MemAlloc and MemFree! + */ + +#include <string.h> +#include <memory.h> +#include <malloc.h> + +#include "types.h" +#include "asrt.h" + +#ifdef ASSERT // Must be after asrt.h! + +#include "mem.h" +#include "mem.msg" + + +/*** MEMSIG - memory signature + * + * This is placed at the front and end of every dynamic memory + * alloction in DEBUG builds. The pointer has to be unaligned for + * RISC machines. + */ +typedef ULONG MEMSIG; /* ms - memory signature */ +typedef MEMSIG UNALIGNED *PMEMSIG; /* pms */ + +#define msHEAD 0x12345678L // Head signature +#define msTAIL 0x87654321L // Tail signature +#define msBAD 0L // Bad signature +#define cmsTAIL 2 // Number of tail signatures + + +typedef struct mh_t { + MEMSIG ms; // Head signature (msHEAD) + unsigned cb; // Size of user block + struct mh_t *pmhNext; // Next block + struct mh_t *pmhPrev; // Previous block + // char ach[?]; // User block; length is cb + // MEMSIG ms[cmsTAIL]; // Tail signature area (msTAIL...) +} MEMHDR; /* mh - memory header */ +typedef MEMHDR *PMEMHDR; /* pmh */ + + +#define PMHFromPV(pv) ((PMEMHDR)((char *)pv - sizeof(MEMHDR))) +#define PVFromPMH(pmh) ((void *)((char *)pmh+sizeof(MEMHDR))) + + +STATIC PMEMHDR pmhList=NULL; // List of memory blocks +STATIC BOOL fDoCheckHeap=TRUE; // TRUE => check heap regularly + + +void MemSetCheckHeap(BOOL f) +{ + fDoCheckHeap = f; +} + + +void MMCheckHeap(char *pszFile, int iLine) +{ + PMEMHDR pmh; + + for (pmh = pmhList; pmh != NULL; pmh = pmh->pmhNext) + MMAssert(PVFromPMH(pmh),pszFile,iLine); +} + + +void MMAssert(void *pv, char *pszFile, int iLine) +{ + int i; + PMEMHDR pmh; + PMEMSIG pms; + + AssertSub(pv!=NULL,pszFile,iLine); + pmh = PMHFromPV(pv); + if ((void *)pmh > pv) { // Pointer wrapped + AssertForce(pszMEMERR_NULL_POINTER,pszFile,iLine); + } + + // Test head signature + if (pmh->ms != msHEAD) { + AssertForce(pszMEMERR_BAD_HEAD_SIG,pszFile,iLine); + } + + // Test tail signatures + pms = (PMEMSIG)( (char *)pmh + sizeof(MEMHDR) + pmh->cb ); + for (i=0; i<cmsTAIL; i++) { + if (*pms++ != msTAIL) { + AssertForce(pszMEMERR_BAD_HEAD_SIG,pszFile,iLine); + } + } +} /* MMAssert */ + + +void MMFree(void *pv, char *pszFile, int iLine) +{ + PMEMHDR pmh; + + MMAssert(pv,pszFile,iLine); + + //** Check heap if enabled + if (fDoCheckHeap) { + MMCheckHeap(pszFile,iLine); + } + + pmh = PMHFromPV(pv); + + // Make previous block point to next block + if (pmh->pmhPrev != NULL) { // pmh is not at front of list + // before: a->p->? + pmh->pmhPrev->pmhNext = pmh->pmhNext; + // after: a->? + } + else { // pmh is at front of list + // before: list->p->? + pmhList = pmh->pmhNext; + // after: list->? + } + + // Make next block point to previous block + if (pmh->pmhNext != NULL) { // pmh is not at end of list + // before: ?<-p<->a + pmh->pmhNext->pmhPrev = pmh->pmhPrev; + // after: ?<-a + } + + // Obliterate signature + pmh->ms = msBAD; + + // Free memory + free((char *)pmh); +} + + +void *MMAlloc(unsigned cb, char *pszFile, int iLine) +{ + unsigned cbAlloc; + int i; + PMEMHDR pmh; + PMEMSIG pms; + + if (fDoCheckHeap) { + MMCheckHeap(pszFile,iLine); + } + + // Solves alignment problems on the RISCs + cb = (cb+3) & ~3; + + cbAlloc = cb+sizeof(MEMHDR)+sizeof(MEMSIG)*cmsTAIL; + pmh = malloc(cbAlloc); + if (pmh != NULL) { + pmh->ms = msHEAD; // Store head signature + pmh->cb = cb; // Store size of user block + + // Add block to front of list (Easiest code!) + if (pmhList != NULL) { // List is not empty + pmhList->pmhPrev = pmh; // Point old top block back at us + } + pmh->pmhNext = pmhList; // Next element is old top block + pmh->pmhPrev = NULL; // We are first, so no prev block + pmhList = pmh; // Make ourselves first + + // Fill in tail signatures + pms = (PMEMSIG)( (char *)pmh + sizeof(MEMHDR) + pmh->cb ); + for (i=0; i<cmsTAIL; i++) { + *pms++ = msTAIL; + } + return PVFromPMH(pmh); + } + else { + AssertForce(pszMEMERR_OUT_OF_MEMORY,pszFile,iLine); +/* + printf("panic: out of memory in MMAlloc\n"); + printf("\n"); + printf("Dump of heap (newest alloc to oldest)\n"); + printf("\n"); + printf("Size Addr Content\n"); + printf("----- ---- -------\n"); + for (pmh = pmhList; pmh != NULL; pmh = pmh->pmhNext) { + pch = PVFromPMH(pmh); + printf("%5d %04x %s\n",pmh->cb,(unsigned)pch,pch); + } + return NULL; +*/ + } +} + + +char *MMStrDup(char *pch, char *pszFile, int iLine) +{ + unsigned cb; + char *pchDst; + + //** Make sure pointer is not null. + // NOTE: pch does not have to be a string we dynamically allocated! + AssertSub(pch!=NULL,pszFile,iLine); + + cb = strlen(pch)+1; // Count NUL terminator + pchDst = MMAlloc(cb,pszFile,iLine); // Alloc new copy + if (pchDst != NULL) { // Success + memcpy(pchDst,pch,cb); // Copy string + } + return pchDst; // Return string copy +} + + +int MemGetSize(void *pv) +{ + PMEMHDR pmh; + + MMAssert(pv,__FILE__,__LINE__); + + pmh = PMHFromPV(pv); + return pmh->cb; +} +#endif // !ASSERT diff --git a/private/windows/diamond/mem.h b/private/windows/diamond/mem.h new file mode 100644 index 000000000..6636c127e --- /dev/null +++ b/private/windows/diamond/mem.h @@ -0,0 +1,74 @@ +/*** mem.h - Definitions for Memory Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * The Memory Manager is a very thin layer on top of the native memory + * heap functions, allowing strong assertion checking and pointer + * validation in debug builds. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 11-Aug-1993 bens Lift code from STOCK.EXE win app + * 12-Aug-1993 bens Improved comments + * 18-Mar-1994 bens strdup() now called _strdup(); renamed + * 18-May-1994 bens Allow turning off MemCheckHeap() in debug build + * (it can really, really slow things down!) + * + * Functions: + * MemAlloc - Allocate memory block + * MemFree - Free memory block + * MemStrDup - Duplicate string to new memory block + * + * Functions available in ASSERT build: + * MemAssert - Assert that pointer was allocated by MemAlloc + * MemCheckHeap - Check entire memory heap + * MemGetSize - Return allocated size of memory block + * MemSetCheckHeap - Control whether MemCheckHeap is done on every + * every MemAlloc and MemFree! + */ + +#ifndef INCLUDED_MEMORY +#define INCLUDED_MEMORY 1 + +#ifdef ASSERT + +#define MemAlloc(cb) MMAlloc(cb,__FILE__,__LINE__) +#define MemFree(pv) MMFree(pv,__FILE__,__LINE__) +#define MemStrDup(pv) MMStrDup(pv,__FILE__,__LINE__) + +#define MemAssert(pv) MMAssert(pv,__FILE__,__LINE__) +#define MemCheckHeap() MMCheckHeap(__FILE__,__LINE__) +int MemGetSize(void *pv); + +void *MMAlloc(unsigned cb, char *pszFile, int iLine); +void MMFree(void *pv, char *pszFile, int iLine); +void MMAssert(void *pv, char *pszFile, int iLine); +void MMCheckHeap(char *pszFile, int iLine); +char *MMStrDup(char *pv, char *pszFile, int iLine); +void MemSetCheckHeap(BOOL f); + +#else // !ASSERT + +#include <malloc.h> // Get malloc()/free() +#include <string.h> // Get _strdup() + + +//** No Asserts + +#define MemAlloc(cb) malloc(cb) +#define MemFree(pv) free(pv) +#define MemStrDup(pv) _strdup(pv) + +#define MemAssert(pv) +#define MemCheckHeap() +#define MemGetSize(pv) +#define MemSetCheckHeap(f) + +#endif // !ASSERT + +#endif // !INCLUDED_MEMORY diff --git a/private/windows/diamond/mem.msg b/private/windows/diamond/mem.msg new file mode 100644 index 000000000..a38e2fa2d --- /dev/null +++ b/private/windows/diamond/mem.msg @@ -0,0 +1,23 @@ +/*** mem.msg - Displayable strings for mem.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 12-Aug-1993 bens Moved from strings.h + */ + +//** Error Messages + +#ifdef ASSERT + +#define pszMEMERR_NULL_POINTER "Null Pointer in MemManager?" +#define pszMEMERR_BAD_HEAD_SIG "Memory block head signature bad" +#define pszMEMERR_BAD_TAIL_SIG "Memory block tail signature bad" +#define pszMEMERR_OUT_OF_MEMORY "Out of memory!" + +#endif // !ASSERT diff --git a/private/windows/diamond/message.c b/private/windows/diamond/message.c new file mode 100644 index 000000000..b0eba9397 --- /dev/null +++ b/private/windows/diamond/message.c @@ -0,0 +1,472 @@ +/*** message.c - Message Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 13-Aug-1993 bens Implemented message formatting + * 21-Feb-1994 bens Return length of formatted string + */ + +#include <ctype.h> +#include <memory.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "types.h" +#include "asrt.h" +#include "mem.h" +#include "message.h" + +#include "message.msg" + + +#ifdef BIT16 + +//** 16-bit build +#ifndef HUGE +#define HUGE huge +#endif + +#ifndef FAR +#define FAR far +#endif + +#else // !BIT16 + +//** Define away for 32-bit (NT/Chicago) build +#ifndef HUGE +#define HUGE +#endif + +#ifndef FAR +#define FAR +#endif + +#endif // !BIT16 + + +typedef enum { + atBAD, + atSHORT, + atINT, + atLONG, + atFLOAT, + atDOUBLE, + atLONGDOUBLE, + atSTRING, + atFARSTRING, +} ARGTYPE; /* at */ + + +int addCommas(char *pszStart); +ARGTYPE ATFromFormatSpecifier(char *pch); +int doFinalSubstitution(char *ach, char *pszMsg, char *apszValue[]); +int getHighestParmNumber(char *pszMsg); + + +/*** MsgSet - Set a message + * + * NOTE: See message.h for entry/exit conditions. + */ +int __cdecl MsgSet(char *ach, char *pszMsg, ...) +{ + int cch; + + va_list marker; // For walking through function arguments + char *pszFmtList; // Format string + + Assert(ach!=NULL); + Assert(pszMsg!=NULL); + + va_start(marker,pszMsg); // Initialize variable arguments + pszFmtList = (char *)va_arg(marker,char *); // Assume format string + + cch = MsgSetWorker(ach,pszMsg,pszFmtList,marker); + va_end(marker); // Done with variable arguments + return cch; +} + + +/*** MsgSetWorker - Set Message after va_start already called + * + * NOTE: See message.h for entry/exit conditions. + * + * Technique: + * 1) Find highest parameter number in pszMsg + * + * If at least one parameter: + * 2) Parse 3rd argument to get sprintf() format strings. + * 3) Pick up each argument and format with sprintf into array + * + * Regardless of parameter count: + * 4) Copy bytes from pszMsg to ach, replacing %N by corresponding + * formatted parameter. + */ +int MsgSetWorker(char *ach, char *pszMsg, char *pszFmtList, va_list marker) +{ + char achFmt[32]; // Temp buffer for single format specifier + char achValues[cbMSG_MAX]; // Buffer of formatted values + ARGTYPE at; // Argument type + char *apszValue[cMSG_PARM_MAX]; // Pointers into achValues + int cch; // Length of format specifier + int cParm; // Highest parameter number + BOOL fCommas; // TRUE=>use Commas + int iParm; // Parameter index + char *pch; // Last character of format specifier + char *pchFmtStart; // Start of single format specifier + char *pszNextValue; // Location in achValues for next value + char *pszStart; + + //** (1) See if we have parameters to retrieve and format + cParm = getHighestParmNumber(pszMsg); + if (cParm > 0) { // Need to get values + //** (2) Parse 3rd argument to get sprintf() format strings. + pszNextValue = achValues; // Start filling at front + pch = pszFmtList; // Start at front of format specifiers + for (iParm=0; iParm<cParm; iParm++) { // Retrieve and format values + apszValue[iParm] = pszNextValue; // Store pointer to formatted value + pchFmtStart = pch; // Remember start of specifier + if (*pch != '%') { // Did not get a format specifier + // Only way to report problem is in output message buffer + strcpy(ach,pszMSGERR_BAD_FORMAT_SPECIFIER); + AssertErrPath(pszMSGERR_BAD_FORMAT_SPECIFIER,__FILE__,__LINE__); + return 0; // Failure + } + //** Find end of specifier + pch++; + while ((*pch != '\0') && (*pch != chMSG)) { + pch++; + } + cch = pch - pchFmtStart; // Length of specifier + if (cch < 2) { // Need at least % and one char for valid specifier + // Only way to report problem is in output message buffer + strcpy(ach,pszMSGERR_SPECIFIER_TOO_SHORT); + AssertErrPath(pszMSGERR_SPECIFIER_TOO_SHORT,__FILE__,__LINE__); + return 0; // Failure + } + + //** (3) Pick up each argument and format with sprintf into array + + //** Get specifier for sprintf() - we need a NULL terminator + fCommas = pchFmtStart[1] == ','; + if (fCommas) { // Copy format, deleting comma + achFmt[0] = pchFmtStart[0]; // Copy '%' + memcpy(achFmt+1,pchFmtStart+2,cch-2); // Get rest after ',' + achFmt[cch-1] = '\0'; // Terminate specifier + } + else { + memcpy(achFmt,pchFmtStart,cch); // Copy to specifier buffer + achFmt[cch] = '\0'; // Terminate specifier + } + + //** Format value, based on last character of format specifier + at = ATFromFormatSpecifier(pch-1); // Get argument type + pszStart = pszNextValue; // Save start of value (for commas) + switch (at) { + case atSHORT: pszNextValue += sprintf(pszNextValue,achFmt, + va_arg(marker,unsigned short)) + 1; + break; + + case atINT: pszNextValue += sprintf(pszNextValue,achFmt, + va_arg(marker,unsigned int)) + 1; + break; + + case atLONG: pszNextValue += sprintf(pszNextValue,achFmt, + va_arg(marker,unsigned long)) + 1; + break; + + case atLONGDOUBLE: pszNextValue += sprintf(pszNextValue,achFmt, +#ifdef BIT16 + va_arg(marker,long double)) + 1; +#else // !BIT16 + //** in 32-bit mode, long double == double + va_arg(marker,double)) + 1; +#endif // !BIT16 + break; + + case atDOUBLE: pszNextValue += sprintf(pszNextValue,achFmt, + va_arg(marker,double)) + 1; + break; + + case atSTRING: pszNextValue += sprintf(pszNextValue,achFmt, + va_arg(marker,char *)) + 1; + break; + + case atFARSTRING: pszNextValue += sprintf(pszNextValue,achFmt, + va_arg(marker,char FAR *)) + 1; + break; + + default: + strcpy(ach,pszMSGERR_UNKNOWN_FORMAT_SPECIFIER); + AssertErrPath(pszMSGERR_UNKNOWN_FORMAT_SPECIFIER,__FILE__,__LINE__); + return 0; // Failure + } /* switch */ + + //** + if (fCommas) { + switch (at) { + case atSHORT: + case atINT: + case atLONG: + pszNextValue += addCommas(pszStart); + break; + } + } + } /* for */ + } /* if - parameters were present */ + + //** (4) Copy bytes from pszMsg to ach, replacing %N parameters with values + return doFinalSubstitution(ach,pszMsg,apszValue); +} + + +/*** addCommas - Add thousand separators to a number + * + * Entry: + * pszStart - Buffer with number at end (NULL terminated) + * NOTE: White space preceding or following number are + * assumed to be part of the field width, and will + * be consumed for use by any commas that are + * added. If there are not enough blanks to account + * for the commas, all the blanks will be consumed, + * and the field will be effectively widened to + * accomodate all of the commas. + * Exit: + * Returns number of commas added (0 or more) + */ +int addCommas(char *pszStart) +{ + char ach[20]; // Buffer for number + int cb; + int cbBlanksBefore; + int cbBlanksAfter; + int cbFirst; + int cCommas; + char *psz; + char *pszSrc; + char *pszDst; + + //** Figure out if there are any blanks + cbBlanksBefore = strspn(pszStart," "); // Count blanks before number + psz = strpbrk(pszStart+cbBlanksBefore," "); // Skip over number + if (psz) { + cbBlanksAfter = strspn(psz," "); // Count blanks after number + cb = psz - (pszStart+cbBlanksBefore); // Length of number itself + } + else { + cbBlanksAfter = 0; // No blanks after number + cb = strlen(pszStart+cbBlanksBefore); // Length of number itself + } + + //** Quick out if we don't need to add commas + if (cb <= 3) { + return 0; + } + //** Figure out how many commas we need to add + Assert(cb < sizeof(ach)); + strncpy(ach,pszStart+cbBlanksBefore,cb); // Move number to a safe place + cCommas = (cb - 1) / 3; // Number of commas we need to add + + //** Figure out where to place modified number in buffer + if ((cbBlanksBefore > 0) && (cbBlanksBefore >= cCommas)) { + //** Eat some (but not all) blanks at front of buffer + pszDst = pszStart + cbBlanksBefore - cCommas; + } + else { + pszDst = pszStart; // Have to start number at front of buffer + } + + //** Add commas to the number + cbFirst = cb % 3; // Number of digits before first comma + if (cbFirst == 0) { + cbFirst = 3; + } + pszSrc = ach; + strncpy(pszDst,pszSrc,cbFirst); + cb -= cbFirst; + pszDst += cbFirst; + pszSrc += cbFirst; + while (cb > 0) { + *pszDst++ = chTHOUSAND_SEPARATOR; // Place comma + strncpy(pszDst,pszSrc,3); // Copy next 3 digits + cb -= 3; + pszDst += 3; + pszSrc += 3; + } + + //** Figure out if we need to add trailing NUL + if (cbBlanksBefore+cbBlanksAfter <= cCommas) { + //** There were no trailing blanks to preserve, so we need to + // make sure the string is terminated. + *pszDst++ = '\0'; // Terminate string + } + + //** Success + return cCommas; +} /* addCommas() */ + + +/*** ATFromFormatSpecifier - Determine argument type from sprintf format + * + * Entry: + * pch - points to last character (type) of sprintf format specifier + * + * Exit-Success: + * Returns ARGTYPE indicated by format specifier. + * + * Exit-Failure: + * Returns atBAD -- could not determine type. + */ +ARGTYPE ATFromFormatSpecifier(char *pch) +{ + switch (*pch) { + case 'c': + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + // Check argument size character + switch (*(pch-1)) { + case 'h': return atSHORT; + case 'l': return atLONG; + default: return atINT; + } + break; + + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + // Check argument size character + switch (*(pch-1)) { + case 'L': return atLONGDOUBLE; + default: // double size +//BUGBUG 13-Aug-1993 bens Should "%f" take a float, and "%lf" take a double? +// The VC++ docs say that "%f" takes a double, but the "l" description says double, +// and that omitting it cause float. I'm confused! + return atDOUBLE; + } + break; + + case 's': + // Check argument size character + switch (*(pch-1)) { + case 'F': return atFARSTRING; + case 'N': return atSTRING; + default: return atSTRING; + } + break; + + default: + return atBAD; + } /* switch */ +} /* ATFromFormatSpecifier */ + + +/*** doFinalSubstitution - Replace %1, %2, etc. with formatted values + * + * Entry: + * ach - Buffer to receive final output + * pszMsg - Message string, possibly with %1, %2, etc. + * apszValue - Values for %1, %2, etc. + * + * Exit-Success: + * Returns length of final text (not including NUL terminator); + * ach filled in with substituted final text. + * + * Exit-Failure: + * ach filled in with explanation of problem. + */ +int doFinalSubstitution(char *ach, char *pszMsg, char *apszValue[]) +{ + int i; + char *pch; + char *pszOut; + + Assert(ach!=NULL); + Assert(pszMsg!=NULL); + + pch = pszMsg; // Start scanning message at front + pszOut = ach; // Fill output buffer from front + while (*pch != '\0') { + if (*pch == chMSG) { // Could be the start of a parameter + pch++; // Skip % + if (isdigit(*pch)) { // We have a parameter! + i = atoi(pch); // Get number + while ( (*pch != '\0') && // Skip to end of string + isdigit(*pch) ) { // or end of number + pch++; // Skip parameter + } + strcpy(pszOut,apszValue[i-1]); // Copy value + pszOut += strlen(apszValue[i-1]); // Advance to end of value + } + else { // Not a digit + *pszOut++ = chMSG; // Copy % + if (*pch == chMSG) { // "%%" + pch++; // Replace "%%" with single "%" + } + else { // Some other character + *pszOut++ = *pch++; // Copy it + } + } + } + else { // Not a parameter + *pszOut++ = *pch++; // Copy character + } + } + *pszOut = '\0'; // Terminate output buffer + return pszOut-ach; // Size of final string (minus NUL) +} + + +/*** getHighestParmNumber - Get number of highest %N string + * + * Entry: + * pszMsg - String which may contain %N (%0, %1, etc.) strings + * + * Exit-Success: + * Returns highest N found in %N string. + */ +int getHighestParmNumber(char *pszMsg) +{ + int i; + int iMax; + char *pch; + + Assert(pszMsg!=NULL); + + iMax = 0; // No parameter seen so far + pch = pszMsg; + while (*pch != '\0') { + if (*pch == chMSG) { // Could be the start of a parameter + pch++; // Skip % + if (isdigit(*pch)) { // We have a parameter! + i = atoi(pch); // Get number + if (i > iMax) // Remember highest parameter number + iMax = i; + while ( (*pch != '\0') && // Skip to end of string + isdigit(*pch) ) { // or end of number + pch++; // Skip parameter + } + } + else { // Not a digit + pch++; // Skip it + } + } + else { // Not a parameter + pch++; // Skip it + } + } + return iMax; // Return highest parameter seen +} diff --git a/private/windows/diamond/message.h b/private/windows/diamond/message.h new file mode 100644 index 000000000..c6548132b --- /dev/null +++ b/private/windows/diamond/message.h @@ -0,0 +1,109 @@ +/*** message.h - Definitions for Message Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 12-Aug-1993 bens Implemented message formatting + * 14-Aug-1993 bens Add MsgSetWorker() for call by ErrSet() + * 21-Feb-1994 bens Return length of formatted string + */ + +#ifndef INCLUDED_MESSAGE +#define INCLUDED_MESSAGE 1 + +#include <stdarg.h> + +//** chMSG - replaceable message character (%1, %2, etc.) +#define chMSG '%' + +//** cbMSG_MAX - Length of largest formatted message +#define cbMSG_MAX 512 + +//** cMSG_PARM_MAX - Maximum number of replaceable parameters +#define cMSG_PARM_MAX 10 + + +/*** MsgSet - Set a message + * + * Entry + * ach - Buffer to receive formatted message + * pszMsg - Message string, possibly including %1, %2, ... replaceable + * parameters. The highest parameter number indicates how + * many sprintf() formatting strings are present in pszFmt. + * If no parameter strings (%1, etc.) are present, then + * pszFmt is not processed. + * + * Remaining arguments are optional, and depend upon presence of %N + * replaceable parameters in pszMsg: + * pszFmt - If at least one %N string in pszMsg, then this contains + * sprintf() formatting strings. There must be at least as + * many formatting strings as the highest parameter string + * number. Excess formatting strings are ignored. + * NOTE: To get thousand separators (,) in numbers, include + * a comma (",") immediately after the "%" for %d + * format specifiers! + * Arg1 - Value for %1. + * Arg2 - Value for %2. + * ... + * + * Exit-Success + * Returns length of string in ach (not including NUL terminator) + * ach filled in with formatted message. + * Arg1 is formatted according to the first sprintf format in + * pszFmt, and replaces the %1 in pszMsg. Similar treatment for + * any other arguments. + * + * Exit-Failure + * Returns 0; + * ach filled in with message describing bad arguments. + * + * Notes: + * (1) "%%" is copied to ach as "%". + * (2) If "%" is not followed by a digit, it is copied to ach. + * + * + * Examples: + * (1) MsgSet(ach,"%1 is %2 months old %3.","%s%d%s","Joe",3,"today"); + * RESULT: ach = "Joe is 3 months old today" + * + * (2) MsgSet(ach,"%3 is %1 months old %2.","%d%s%s",3,"today","Joe"); + * RESULT: ach = "Joe is 3 months old today" + * + * (3) MsgSet(ach,"%1 bytes","%,d",123456789L); + * RESULT: ach = "123,456,789 bytes" + */ +int __cdecl MsgSet(char *ach, char *pszMsg, ...); + + +/*** MsgSetWorker - Set Message after va_start already called + * + * NOTE: See MsgSet for other details about behavior. + * + * Entry + * ach - Buffer to receive formatted message + * pszMsg - Message string (see MsgSet); + * pszFmt - Format string (see MsgSet); + * marker - Initialized by call to va_start + * + * Exit-Success + * Returns length of string in ach (not including NUL terminator) + * ach filled in with formatted message. + * Arg1 is formatted according to the first sprintf format in + * pszFmt, and replaces the %1 in pszMsg. Similar treatment for + * any other arguments. + * + * Exit-Failure + * Returns 0; + * perr filled in with message describing bad arguments. + * RESULT: ach = "Joe is 3 months old today" + */ +int MsgSetWorker(char *ach, char *pszMsg, char *pszFmtList, va_list marker); + +#endif // !INCLUDED_MESSAGE + diff --git a/private/windows/diamond/message.msg b/private/windows/diamond/message.msg new file mode 100644 index 000000000..4749e083f --- /dev/null +++ b/private/windows/diamond/message.msg @@ -0,0 +1,21 @@ +/*** message.msg - Displayable strings for message.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 13-Aug-1993 bens Created messages + * 14-Aug-1993 bens Added more error cases + * 06-May-1994 bens Added thousands separator + */ + +#define chTHOUSAND_SEPARATOR ',' // 123,456,789.00 + + +#define pszMSGERR_BAD_FORMAT_SPECIFIER "[INTERR] Format specifier missing %" +#define pszMSGERR_SPECIFIER_TOO_SHORT "[INTERR] Format specifier %% is invalid" +#define pszMSGERR_UNKNOWN_FORMAT_SPECIFIER "[INTERR] Format specifier has unknown type" diff --git a/private/windows/diamond/misc.c b/private/windows/diamond/misc.c new file mode 100644 index 000000000..19123737e --- /dev/null +++ b/private/windows/diamond/misc.c @@ -0,0 +1,151 @@ +/*** misc.c - Miscellaneous functions for DIAMOND.EXE + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 25-Apr-1994 bens Initial version + * 11-Jul-1994 bens copyBounded was undercounting by 1 byte + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" + +#include "fileutil.h" + +#include "misc.h" +#include "misc.msg" +#include "dfparse.msg" + + +/*** nameFromTemplate - Construct name from template with * and integer + * + * NOTE: see misc.h for entry/exit conditions. + */ +BOOL nameFromTemplate(char *pszResult, + int cbResult, + char *pszTemplate, + int i, + char *pszName, + PERROR perr) +{ + char ach[cbFILE_NAME_MAX]; // Buffer for resulting name + char achFmt[cbFILE_NAME_MAX]; // Buffer for sprintf format string + int cch; + int cWildCards=0; // Count of wild cards + char *pch; + char *pchDst; + + //** Replace any wild card characters with %d + pchDst = achFmt; + for (pch=pszTemplate; *pch; pch++) { + if (*pch == chDF_WILDCARD) { // Got a wild card + *pchDst++ = '%'; // Replace with %d format specifier + *pchDst++ = 'd'; + cWildCards++; // Count how many we see + } + else { + *pchDst++ = *pch; // Copy character + } + } + *pchDst++ = '\0'; // Terminate string + + if (cWildCards > 4) { + ErrSet(perr,pszMISCERR_TWO_MANY_WILDS,"%c%d%s", + chDF_WILDCARD,4,pszName); + return FALSE; + } + + //** Replace first four(4) occurences (just to be hard-coded!) + cch = sprintf(ach,achFmt,i,i,i,i); + + //** Fail if expanded result is too long + if (cch >= cbResult) { + ErrSet(perr,pszMISCERR_EXPANDED_TOO_LONG,"%s%d%s", + pszName,cbResult-1,ach); + return FALSE; + } + strcpy(pszResult,ach); + + //** Success + return TRUE; +} /* nameFromTemplate() */ + + +/*** copyBounded - Copy bytes from src to dst, checking for overflow + * + * Entry: + * ppszDst - pointer to pointer to destination buffer + * pcbDst - pointer to bytes remaining in destination buffer + * ppszSrc - pointer to pointer to source buffer + * cbCopy - Number of bytes to copy + * ==> 0 means copy to end of ppszSrc, including NULL terminator + * + * Exit-Success: + * Returns TRUE; Bytes copied; *ppszDst, *pcbDst, and *ppszSrc updated. + * + * Exit-Failure: + * Returns FALSE; *ppszDst overflowed. + */ +BOOL copyBounded(char **ppszDst, int *pcbDst, char **ppszSrc, int cbCopy) +{ + char *pszDst = *ppszDst; + int cbDst = *pcbDst; + char *pszSrc = *ppszSrc; + + if (cbCopy == 0) { + //** Copy to end of source string + // NOTE: I know the "," operator is pretty obscure, but this + // was the most straightforward way I could figure out + // to do the string copy and keep cbDst up to date! + while ((cbDst > 0) && (cbDst--, *pszDst++ = *pszSrc++)) { + } + + //** Make sure we didn't overflow buffer + if (pszSrc[-1] != '\0') { // Oops, didn't get all of source + return FALSE; + } + } + else { + //** Copy specified number of bytes or until end of + // source string or out of space in destination buffer. + // NOTE: I know the "," operator is pretty obscure, but this + // was the most straightforward way I could figure out + // to do the string copy and keep cbDst and cbCopy up to date! + while ((cbCopy>0) && + (cbDst>0) && + (cbCopy--, cbDst--, *pszDst++ = *pszSrc++)) { + } + + //** See if we copied all the bytes requested + if (0 < cbCopy) { // Did not copy all the bytes + //** Check if a NULL byte terminated the copy + if (pszSrc[-1] == '\0') { + AssertForce("copyBounded(): string has NULL byte", + __FILE__, __LINE__); + } + return FALSE; // Failure + } + } + + //** Update caller's parameters and return success + Assert((pszDst - *ppszDst) == (*pcbDst - cbDst)); + *ppszDst = pszDst; + *pcbDst = cbDst; + *ppszSrc = pszSrc; + + return TRUE; +} /* copyBounded */ diff --git a/private/windows/diamond/misc.h b/private/windows/diamond/misc.h new file mode 100644 index 000000000..b529d0b83 --- /dev/null +++ b/private/windows/diamond/misc.h @@ -0,0 +1,58 @@ +/*** misc.h - Definitions for miscellaneous functions + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 25-Apr-1994 bens Initial version + */ + +#ifndef INCLUDED_MISC +#define INCLUDED_MISC 1 + +/*** nameFromTemplate - Construct name from template with * and integer + * + * Entry: + * pszResult - Buffer to receive constructed name + * cbResult - Size of pszResult + * pszTemplate - Template string (with 0 or more "*" characters) + * i - Value to use in place of "*" + * pszName - Name to use if error is detected + * perr - ERROR structure to fill in + * + * Exit-Success: + * Returns TRUE; pszResult filled in. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL nameFromTemplate(char * pszResult, + int cbResult, + char * pszTemplate, + int i, + char * pszName, + PERROR perr); + + +/*** copyBounded - Copy bytes from src to dst, checking for overflow + * + * Entry: + * ppszDst - pointer to pointer to destination buffer + * pcbDst - pointer to bytes remaining in destination buffer + * ppszSrc - pointer to pointer to source buffer + * cbCopy - Number of bytes to copy + * ==> 0 means copy to end of ppszSrc, including NULL terminator + * + * Exit-Success: + * Returns TRUE; Bytes copied; *ppszDst, *pcbDst, and *ppszSrc updated. + * + * Exit-Failure: + * Returns FALSE; *ppszDst overflowed. + */ +BOOL copyBounded(char **ppszDst, int *pcbDst, char **ppszSrc, int cbCopy); + +#endif // !INCLUDED_MISC diff --git a/private/windows/diamond/misc.msg b/private/windows/diamond/misc.msg new file mode 100644 index 000000000..a1b962fa2 --- /dev/null +++ b/private/windows/diamond/misc.msg @@ -0,0 +1,14 @@ +/*** misc.msg - Displayable strings for misc.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 25-Apr-1994 bens Initial version + */ + +//** Error messages + +#define pszMISCERR_TWO_MANY_WILDS "Wildcard (%1) limit (%2) exceeded in %3" +#define pszMISCERR_EXPANDED_TOO_LONG "%1 length exceeded limit (%2): %3" diff --git a/private/windows/diamond/mszip/dirs b/private/windows/diamond/mszip/dirs new file mode 100644 index 000000000..44838dc17 --- /dev/null +++ b/private/windows/diamond/mszip/dirs @@ -0,0 +1,2 @@ +DIRS=mci.nt mdi.nt +
\ No newline at end of file diff --git a/private/windows/diamond/mszip/mci.c b/private/windows/diamond/mszip/mci.c new file mode 100644 index 000000000..347dc1333 --- /dev/null +++ b/private/windows/diamond/mszip/mci.c @@ -0,0 +1,305 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * MCI.C: Memory Compression Interface + * + * History: + * 20-Jan-1994 msliger Initial version (stub). + * 23-Jan-1994 msliger Real version (not a stub). + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * normalized MCI_MEMORY type. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * Changed MCI_MEMORY to MI_MEMORY. + * Restructured allocation/destruction. + * 15-Mar-1994 msliger Changes for 32 bits. + * 22-Mar-1994 msliger Changed interface USHORT to UINT. + * 26-Sep-1994 msliger Made FAR's match MCI.H. + */ + +/* --- preprocessor ------------------------------------------------------- */ + +#include <stdio.h> /* for NULL */ +#include <string.h> /* for memcpy() */ + +#include "mci.h" /* types, prototype verification, error codes */ + +#include "nfmcomp.h" /* features of NFMCOMP.C */ + +/* MAKE_SIGNATURE - Construct a structure signature + * + * Entry: + * a,b,c,d - four characters + * + * Exit: + * Returns constructed SIGNATURE + * + * Example: + * strct->signature = MAKE_SIGNATURE('b','e','n','s') + */ + +#define MAKE_SIGNATURE(a,b,c,d) (a + (b<<8) + (c<<16) + (d<<24)) +#define BAD_SIGNATURE (0L) +#define MCI_SIGNATURE MAKE_SIGNATURE('M','C','I','C') + +/* --- MCI context structure ---------------------------------------------- */ + +typedef ULONG SIGNATURE; /* structure signature */ + +struct MCI_CONTEXT /* private structure */ +{ + SIGNATURE signature; /* for validation */ + PFNFREE pfnFree; /* where the free() is */ + UINT cbDataBlockMax; /* promised max data size */ + MI_MEMORY buff1; /* work buffer */ + MI_MEMORY buff2; /* work buffer */ + MI_MEMORY work1; /* work buffer */ + MI_MEMORY work2; /* work buffer */ +#ifdef LGM + MI_MEMORY work3; /* work buffer */ + MI_MEMORY work4; /* work buffer */ +#endif + MI_MEMORY historyBuffer; /* history buffer */ + char history; /* history is valid flag */ +}; + +typedef struct MCI_CONTEXT FAR *PMCC_CONTEXT; /* a pointer to one */ + +#define PMCCfromHMC(h) ((PMCC_CONTEXT)(h)) /* handle to pointer */ +#define HMCfromPMCC(p) ((MCI_CONTEXT_HANDLE)(p)) /* pointer to handle */ + +/* --- MCICreateCompression() --------------------------------------------- */ + +int FAR DIAMONDAPI MCICreateCompression( + UINT FAR * pcbDataBlockMax, /* max uncompressed data block */ + PFNALLOC pfnma, /* Memory allocation function */ + PFNFREE pfnmf, /* Memory free function */ + UINT FAR * pcbDstBufferMin, /* gets required output buffer */ + MCI_CONTEXT_HANDLE FAR *pmchHandle) /* gets newly-created handle */ +{ + PMCC_CONTEXT context; /* new context */ + + *pmchHandle = (MCI_CONTEXT_HANDLE) 0; /* wait until it's valid */ + + if ((*pcbDataBlockMax == 0) || (*pcbDataBlockMax > 32768u)) + { + *pcbDataBlockMax = 32768u; /* help with source block size */ + } + + context = pfnma(sizeof(struct MCI_CONTEXT)); + if (context == NULL) + { + return(MCI_ERROR_NOT_ENOUGH_MEMORY); /* if can't allocate */ + } + + context->signature = MCI_SIGNATURE; + context->history = 0; /* no compression history here */ + context->pfnFree = pfnmf; /* remember where free() is */ + context->cbDataBlockMax = *pcbDataBlockMax; /* remember agreement */ + + *pcbDstBufferMin = /* we'll expand sometimes */ + *pcbDataBlockMax + MAX_GROWTH; + + + /* allocate all buffers */ + + context->buff1 = pfnma(LIT_BUFSIZE); /* literal buffer */ + context->buff2 = pfnma(DIST_BUFSIZE); /* distance buffer */ + +#ifdef LGM + context->work1 = pfnma(2*256); /* one-char match heads */ + context->work2 = pfnma(2*32768L); /* one-char list */ + context->work3 = pfnma(2*32768L); /* two-char list */ + context->work4 = pfnma(2*32768L); /* three-char list */ +#else + context->work1 = pfnma(2*32768L); /* hash table */ + context->work2 = pfnma(2*32768L); /* hash match links */ +#endif + + context->historyBuffer = pfnma(65536L); /* history data buffer */ + + if ((context->buff1 == NULL) || /* if any allocations failed */ + (context->buff2 == NULL) || + (context->work1 == NULL) || + (context->work2 == NULL) || +#ifdef LGM + (context->work3 == NULL) || + (context->work4 == NULL) || +#endif + (context->historyBuffer == NULL)) + { + MCIDestroyCompression(HMCfromPMCC(context)); /* nuke it */ + + return(MCI_ERROR_NOT_ENOUGH_MEMORY); /* report failed */ + } + + + if (NFMcompress_init(context->buff1,context->buff2) != NFMsuccess) + { + MCIDestroyCompression(HMCfromPMCC(context)); + + return(MCI_ERROR_NOT_ENOUGH_MEMORY); + } + + + /* pass context back to caller */ + + *pmchHandle = HMCfromPMCC(context); + + return(MCI_ERROR_NO_ERROR); +} + +/* --- MCICompress() ------------------------------------------------------ */ + +int FAR DIAMONDAPI MCICompress( + MCI_CONTEXT_HANDLE hmc, /* compression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source actual size */ + void FAR * pbDst, /* target buffer */ + UINT cbDst, /* size of target buffer */ + UINT FAR * pcbResult) /* gets target actual size */ +{ + PMCC_CONTEXT context; /* pointer to the context */ + void FAR * sourcePointer; /* used to copy data */ + int result; /* return code */ + + context = PMCCfromHMC(hmc); /* get pointer from handle */ + + if (context->signature != MCI_SIGNATURE) + { + return(MCI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + if (cbSrc > context->cbDataBlockMax) + { + return(MCI_ERROR_BAD_PARAMETERS); /* violated max block promise */ + } + + if (cbDst < (context->cbDataBlockMax + MAX_GROWTH)) + { + return(MCI_ERROR_BAD_PARAMETERS); /* violated min buffer request */ + } + + if (context->history) + { + sourcePointer = ((BYTE FAR *) context->historyBuffer) + (32 * 1024L); + } + else + { + sourcePointer = context->historyBuffer; + } + +#ifdef BIT16 + _fmemcpy(sourcePointer,pbSrc,cbSrc); +#else + memcpy(sourcePointer,pbSrc,cbSrc); +#endif + + result = NFMcompress(context->historyBuffer,cbSrc,pbDst,cbDst, + context->work1, context->work2, +#ifdef LGM + context->work3, context->work4, +#endif + context->history, pcbResult); + + if (cbSrc == 32768u) + { + context->history = 1; /* now we have history */ + } + else + { + context->history = 0; /* no history after < 32K */ + } + + if (result) + { + return(MCI_ERROR_FAILED); /* report failure */ + } + else + { + return(MCI_ERROR_NO_ERROR); /* report no failure */ + } +} + +/* --- MCIResetCompression() ---------------------------------------------- */ + +int DIAMONDAPI MCIResetCompression(MCI_CONTEXT_HANDLE hmc) +{ + PMCC_CONTEXT context; /* pointer to the context */ + + context = PMCCfromHMC(hmc); /* get pointer from handle */ + + if (context->signature != MCI_SIGNATURE) + { + return(MCI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + context->history = 0; /* no compression history here */ + + return(MCI_ERROR_NO_ERROR); /* if tag is OK */ +} + +/* --- MCIDestroyCompression() -------------------------------------------- */ + +int DIAMONDAPI MCIDestroyCompression(MCI_CONTEXT_HANDLE hmc) +{ + PMCC_CONTEXT context; /* pointer to context */ + register PFNFREE pfree; /* quick pointer */ + + context = PMCCfromHMC(hmc); /* get pointer from handle */ + + if (context->signature != MCI_SIGNATURE) + { + return(MCI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + context->signature = BAD_SIGNATURE; /* destroy signature */ + + pfree = context->pfnFree; /* get pointer from struct */ + + if (context->buff1 != NULL) + { + pfree(context->buff1); + } + + if (context->buff2 != NULL) + { + pfree(context->buff2); + } + + if (context->work1 != NULL) + { + pfree(context->work1); + } + + if (context->work2 != NULL) + { + pfree(context->work2); + } + +#ifdef LGM + if (context->work3 != NULL) + { + pfree(context->work3); + } + + if (context->work4 != NULL) + { + pfree(context->work4); + } +#endif + + if (context->historyBuffer != NULL) + { + pfree(context->historyBuffer); /* self-destruct */ + } + + pfree(context); + + return(MCI_ERROR_NO_ERROR); /* success */ +} + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/mszip/mci.h b/private/windows/diamond/mszip/mci.h new file mode 100644 index 000000000..b74b8f390 --- /dev/null +++ b/private/windows/diamond/mszip/mci.h @@ -0,0 +1,250 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993,1994 + * All Rights Reserved. + * + * MCI.H - Diamond Memory Compression Interface (MCI) + * + * History: + * 01-Dec-1993 bens Initial version. + * 16-Jan-1994 msliger Split into MCI, MDI. + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * normalized MCI_MEMORY type. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * Changed HANDLE to MHANDLE. + * Changed MCI_MEMORY to MI_MEMORY. + * 15-Mar-1994 msliger Changes for 32 bits. + * 22-Mar-1994 msliger Changed !INT32 to BIT16. + * Changed interface USHORT to UINT. + * + * Functions: + * MCICreateCompression - Create and reset an MCI compression context + * MCICloneCompression - Make a copy of a compression context + * MCICompress - Compress a block of data + * MCIResetCompression - Reset compression context + * MCIDestroyCompression - Destroy MCI compression context + * + * Types: + * MCI_CONTEXT_HANDLE - Handle to an MCI compression context + * PFNALLOC - Memory allocation function for MCI + * PFNFREE - Free memory function for MCI + */ + +/* --- types -------------------------------------------------------------- */ + +#ifndef DIAMONDAPI +#define DIAMONDAPI __cdecl +#endif + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif + +#ifndef _UINT_DEFINED +#define _UINT_DEFINED +typedef unsigned int UINT; +#endif + +#ifndef _ULONG_DEFINED +#define _ULONG_DEFINED +typedef unsigned long ULONG; +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE huge +#else +#define HUGE +#endif +#endif + +#ifndef _MI_MEMORY_DEFINED +#define _MI_MEMORY_DEFINED +typedef void HUGE * MI_MEMORY; +#endif + +#ifndef _MHANDLE_DEFINED +#define _MHANDLE_DEFINED +typedef unsigned long MHANDLE; +#endif + +/* --- MCI-defined types -------------------------------------------------- */ + +/* MCI_CONTEXT_HANDLE - Handle to an MCI compression context */ + +typedef MHANDLE MCI_CONTEXT_HANDLE; /* hmc */ + + +/*** PFNALLOC - Memory allocation function for MCI + * + * Entry: + * cb - Size in bytes of memory block to allocate + * + * Exit-Success: + * Returns !NULL pointer to memory block + * + * Exit-Failure: + * Returns NULL; insufficient memory + */ +#ifndef _PFNALLOC_DEFINED +#define _PFNALLOC_DEFINED +typedef MI_MEMORY (FAR DIAMONDAPI *PFNALLOC)(ULONG cb); /* pfnma */ +#endif + + +/*** PFNFREE - Free memory function for MCI + * + * Entry: + * pv - Memory block allocated by matching PFNALLOC function + * + * Exit: + * Memory block freed. + */ +#ifndef _PFNFREE_DEFINED +#define _PFNFREE_DEFINED +typedef void (FAR DIAMONDAPI *PFNFREE)(MI_MEMORY pv); /* pfnmf */ +#endif + +/* --- prototypes --------------------------------------------------------- */ + +/*** MCICreateCompression - Create MCI compression context + * + * Entry: + * pcbDataBlockMax *largest uncompressed data block size desired, + * gets largest uncompressed data block allowed + * pfnma memory allocation function pointer + * pfnmf memory free function pointer + * pcbDstBufferMin gets required compressed data buffer size + * pmchHandle gets newly-created context's handle + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * *pcbDataBlockMax, *pcbDstBufferMin, *pmchHandle filled in. + * + * Exit-Failure: + * MCI_ERROR_NOT_ENOUGH_MEMORY, could not allocate enough memory. + * MCI_ERROR_BAD_PARAMETERS, something wrong with parameters. + */ +int FAR DIAMONDAPI MCICreateCompression( + UINT FAR * pcbDataBlockMax, /* max uncompressed data block size */ + PFNALLOC pfnma, /* Memory allocation function ptr */ + PFNFREE pfnmf, /* Memory free function ptr */ + UINT FAR * pcbDstBufferMin, /* gets required output buffer size */ + MCI_CONTEXT_HANDLE FAR *pmchHandle); /* gets newly-created handle */ + + +/*** MCICloneCompression - Make a copy of a compression context + * + * Entry: + * hmc handle to current compression context + * pmchHandle gets newly-created handle + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * *pmchHandle filled in. + * + * Exit-Failure: + * Returns: + * MCI_ERROR_BAD_PARAMETERS, something wrong with parameters. + * MCI_ERROR_NOT_ENOUGH_MEMORY, could not allocate enough memory. + * + * NOTES: + * (1) This API is intended to permit "roll-back" of a sequence of + * of MCICompress() calls. Before starting a sequence that may need + * to be rolled-back, use MCICloneCompression() to save the state of + * the compression context, then do the MCICompress() calls. If the + * sequence is successful, the "cloned" hmc can be destroyed with + * MCIDestroyCompression(). If the sequence is *not* successful, then + * the original hmc can be destroyed, and the cloned one can be used + * to restart as if the sequence of MCICompress() calls had never + * occurred. + */ +int FAR DIAMONDAPI MCICloneCompression( + MCI_CONTEXT_HANDLE hmc, /* current compression context */ + MCI_CONTEXT_HANDLE *pmchHandle); /* gets newly-created handle */ + + +/*** MCICompress - Compress a block of data + * + * Entry: + * hmc handle to compression context + * pbSrc source buffer (uncompressed data) + * cbSrc size of data to be compressed + * pbDst destination buffer (for compressed data) + * cbDst size of destination buffer + * pcbResult receives compressed size of data + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * *pcbResult has size of compressed data in pbDst. + * Compression context possibly updated. + * + * Exit-Failure: + * MCI_ERROR_BAD_PARAMETERS, something wrong with parameters. + */ +int FAR DIAMONDAPI MCICompress( + MCI_CONTEXT_HANDLE hmc, /* compression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source buffer size */ + void FAR * pbDst, /* target buffer */ + UINT cbDst, /* target buffer size */ + UINT FAR * pcbResult); /* gets target data size */ + + +/*** MCIResetCompression - Reset compression history (if any) + * + * De-compression can only be started on a block which was compressed + * immediately following a MCICreateCompression() or MCIResetCompression() + * call. This function forces such a new "compression boundary" to be + * created (only by causing the compressor to ignore history, can the data + * output be decompressed without history.) + * + * Entry: + * hmc - handle to compression context + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * Compression context reset. + * + * Exit-Failure: + * Returns MCI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI MCIResetCompression(MCI_CONTEXT_HANDLE hmc); + + +/*** MCIDestroyCompression - Destroy MCI compression context + * + * Entry: + * hmc - handle to compression context + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * Compression context destroyed. + * + * Exit-Failure: + * Returns MCI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI MCIDestroyCompression(MCI_CONTEXT_HANDLE hmc); + +/* --- constants ---------------------------------------------------------- */ + +/* return codes */ + +#define MCI_ERROR_NO_ERROR 0 +#define MCI_ERROR_NOT_ENOUGH_MEMORY 1 +#define MCI_ERROR_BAD_PARAMETERS 2 +#define MCI_ERROR_BUFFER_OVERFLOW 3 +#define MCI_ERROR_FAILED 4 + +/* ----------------------------------------------------------------------- */ diff --git a/private/windows/diamond/mszip/mci.nt/makefile b/private/windows/diamond/mszip/mci.nt/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/mszip/mci.nt/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/mszip/mci.nt/sources b/private/windows/diamond/mszip/mci.nt/sources new file mode 100644 index 000000000..3222e02e4 --- /dev/null +++ b/private/windows/diamond/mszip/mci.nt/sources @@ -0,0 +1,37 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Ted Miller (tedm) 19-Feb-1991 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=diamond +MINORCOMP=mszip_mci + +TARGETNAME=mci +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +C_DEFINES=-DLARGE_STORED_BLOCKS + +SOURCES=..\mci.c \ + ..\nfmcomp.c + +UMTYPE=console diff --git a/private/windows/diamond/mszip/mdi.c b/private/windows/diamond/mszip/mdi.c new file mode 100644 index 000000000..8d6530845 --- /dev/null +++ b/private/windows/diamond/mszip/mdi.c @@ -0,0 +1,225 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994,1995 + * All Rights Reserved. + * + * MDI.C: Memory Decompression Interface + * + * History: + * 20-Jan-1994 msliger Initial version. + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * 22-Mar-1994 msliger Changed interface USHORT to UINT. + * 15-Nov-1994 msliger NFMDECO no longer needs alloc/free. + * 31-Jan-1995 msliger Supported MDICreateDecompression query. + * 25-May-1995 msliger Dropped NFMuncompress, using NFM_Prepare() + * and NFM_Decompress(). Added INCR_TESTING. + * + * Compilation options: + * + * INCR_TESTING define to some number to cause incremental decompression + * (of not more than that many bytes per pass) + * + */ + +/* --- preprocessor ------------------------------------------------------- */ + +#include <stdio.h> /* for NULL */ + +#include "mdi.h" /* types, prototype verification, error codes */ + +#include "nfmdeco.h" /* features of NFMDECO.C */ + +/* MAKE_SIGNATURE - Construct a structure signature + * + * Entry: + * a,b,c,d - four characters + * + * Exit: + * Returns constructed SIGNATURE + * + * Example: + * strct->signature = MAKE_SIGNATURE('b','e','n','s') + */ + +#define MAKE_SIGNATURE(a,b,c,d) (a + (b<<8) + (c<<16) + (d<<24)) +#define BAD_SIGNATURE (0L) +#define MDI_SIGNATURE MAKE_SIGNATURE('M','D','I','C') + +/* --- MDI context structure ---------------------------------------------- */ + +typedef ULONG SIGNATURE; /* structure signature */ + +struct MDI_CONTEXT /* private structure */ +{ + SIGNATURE signature; /* for validation */ + PFNALLOC pfnAlloc; /* where the alloc() is */ + PFNFREE pfnFree; /* where the free() is */ + UINT cbDataBlockMax; /* promised max data size */ + void *DecompContext; +}; + +typedef struct MDI_CONTEXT FAR *PMDC_CONTEXT; /* a pointer to one */ + +#define PMDCfromHMD(h) ((PMDC_CONTEXT)(h)) /* handle to pointer */ +#define HMDfromPMDC(p) ((MDI_CONTEXT_HANDLE)(p)) /* pointer to handle */ + +/* --- MDICreateDecompression() ------------------------------------------- */ + +int DIAMONDAPI MDICreateDecompression( + UINT * pcbDataBlockMax, /* max uncompressed data block */ + PFNALLOC pfnma, /* Memory allocation function */ + PFNFREE pfnmf, /* Memory free function */ + UINT * pcbSrcBufferMin, /* gets required input buffer */ + MDI_CONTEXT_HANDLE * pmdhHandle) /* gets newly-created handle */ +{ + PMDC_CONTEXT context; /* new context */ + + if ((*pcbDataBlockMax == 0) || (*pcbDataBlockMax > 32768u)) + { + *pcbDataBlockMax = 32768u; /* help with source block size */ + } + + *pcbSrcBufferMin = /* we'll expand sometimes */ + *pcbDataBlockMax + MAX_GROWTH; + + if (pmdhHandle == NULL) /* if no context requested, */ + { + return(MDI_ERROR_NO_ERROR); /* return from query mode */ + } + + *pmdhHandle = (MDI_CONTEXT_HANDLE) 0; /* wait until it's valid */ + + context = pfnma(sizeof(struct MDI_CONTEXT)); + if (context == NULL) + { + return(MDI_ERROR_NOT_ENOUGH_MEMORY); /* if can't allocate */ + } + + context->DecompContext = NFMInitializeContext(pfnma); + if(!context->DecompContext) { + pfnmf(context); + return(MDI_ERROR_NOT_ENOUGH_MEMORY); /* if can't allocate */ + } + + context->pfnAlloc = pfnma; /* remember where alloc() is */ + context->pfnFree = pfnmf; /* remember where free() is */ + context->cbDataBlockMax = *pcbDataBlockMax; /* remember agreement */ + context->signature = MDI_SIGNATURE; /* install signature */ + + *pmdhHandle = HMDfromPMDC(context); /* pass context back to caller */ + + return(MDI_ERROR_NO_ERROR); /* tell caller all is well */ +} + +/* --- MDIDecompress() ---------------------------------------------------- */ + +int DIAMONDAPI MDIDecompress( + MDI_CONTEXT_HANDLE hmd, /* decompression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source actual size */ + void FAR * pbDst, /* target buffer */ + UINT * pcbResult) /* gets actual target size */ +{ + PMDC_CONTEXT context; /* pointer to the context */ + int result; /* return code */ + + context = PMDCfromHMD(hmd); /* get pointer from handle */ + + if (context->signature != MDI_SIGNATURE) + { + return(MDI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + if (cbSrc > (context->cbDataBlockMax + MAX_GROWTH)) + { + return(MDI_ERROR_BUFFER_OVERFLOW); /* violated max block promise */ + } + + result = NFM_Prepare(context->DecompContext, pbSrc, cbSrc, pbDst, context->cbDataBlockMax); + if (result == 0) + { +#ifndef INCR_TESTING + + result = NFM_Decompress(context->DecompContext,pcbResult); + +#else /* INCR_TESTING */ + + UINT cbChunk; + UINT cbActual; + + cbActual = 0; + + do + { + if ((*pcbResult - cbActual) < INCR_TESTING) + { + cbChunk = *pcbResult - cbActual; + } + else + { + cbChunk = INCR_TESTING; + } + + result = NFM_Decompress(context->DecompContext,&cbChunk); + + cbActual += cbChunk; + + } while ((result == 0) && (cbActual < *pcbResult)); + + *pcbResult = cbActual; + +#endif /* INCR_TESTING */ + } + + if (result == 0) + { + return(MDI_ERROR_NO_ERROR); /* report no failure */ + } + else + { + return(MDI_ERROR_FAILED); /* report failure */ + } +} + +/* --- MDIResetDecompression() -------------------------------------------- */ + +int DIAMONDAPI MDIResetDecompression(MDI_CONTEXT_HANDLE hmd) +{ + PMDC_CONTEXT context; /* pointer to the context */ + + context = PMDCfromHMD(hmd); /* get pointer from handle */ + + if (context->signature != MDI_SIGNATURE) + { + return(MDI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + return(MDI_ERROR_NO_ERROR); /* if tag is OK */ +} + +/* --- MDIDestroyDecompression() ------------------------------------------ */ + +int DIAMONDAPI MDIDestroyDecompression(MDI_CONTEXT_HANDLE hmd) +{ + PMDC_CONTEXT context; /* pointer to the context */ + + context = PMDCfromHMD(hmd); /* get pointer from handle */ + + if (context->signature != MDI_SIGNATURE) + { + return(MDI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + context->signature = BAD_SIGNATURE; /* destroy signature */ + + NFMDestroyContext(context->DecompContext,context->pfnFree); + + context->pfnFree(context); /* self-destruct */ + + return(MDI_ERROR_NO_ERROR); /* success */ +} + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/mszip/mdi.h b/private/windows/diamond/mszip/mdi.h new file mode 100644 index 000000000..32ae14e1d --- /dev/null +++ b/private/windows/diamond/mszip/mdi.h @@ -0,0 +1,222 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993,1994,1995 + * All Rights Reserved. + * + * MDI.H - Diamond Memory Decompression Interface (MDI) + * + * History: + * 01-Dec-1993 bens Initial version. + * 16-Jan-1994 msliger Split into MCI, MDI. + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * normalized MDI_MEMORY type. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * Changed HANDLE to MHANDLE. + * Changed MDI_MEMORY to MI_MEMORY. + * 22-Mar-1994 msliger Changed !INT32 to BIT16. + * Changed interface USHORT to UINT. + * 31-Jan-1995 msliger Supported MDICreateDecompression query. + * 25-May-1995 msliger Clarified *pcbResult on entry. + * + * Functions: + * MDICreateDecompression - Create and reset an MDI decompression context + * MDIDecompress - Decompress a block of data + * MDIResetDecompression - Reset MDI decompression context + * MDIDestroyDecompression - Destroy MDI Decompression context + * + * Types: + * MDI_CONTEXT_HANDLE - Handle to an MDI decompression context + * PFNALLOC - Memory allocation function for MDI + * PFNFREE - Free memory function for MDI + */ + +/* --- types -------------------------------------------------------------- */ + +#ifndef DIAMONDAPI +#define DIAMONDAPI __cdecl +#endif + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif + +#ifndef _UINT_DEFINED +#define _UINT_DEFINED +typedef unsigned int UINT; +#endif + +#ifndef _ULONG_DEFINED +#define _ULONG_DEFINED +typedef unsigned long ULONG; +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE huge +#else +#define HUGE +#endif +#endif + +#ifndef _MI_MEMORY_DEFINED +#define _MI_MEMORY_DEFINED +typedef void HUGE * MI_MEMORY; +#endif + +#ifndef _MHANDLE_DEFINED +#define _MHANDLE_DEFINED +typedef unsigned long MHANDLE; +#endif + +/* --- MDI-defined types -------------------------------------------------- */ + +/* MDI_CONTEXT_HANDLE - Handle to an MDI decompression context */ + +typedef MHANDLE MDI_CONTEXT_HANDLE; /* hmd */ + + +/*** PFNALLOC - Memory allocation function for MDI + * + * Entry: + * cb - Size in bytes of memory block to allocate + * + * Exit-Success: + * Returns !NULL pointer to memory block + * + * Exit-Failure: + * Returns NULL; insufficient memory + */ +#ifndef _PFNALLOC_DEFINED +#define _PFNALLOC_DEFINED +typedef MI_MEMORY (FAR DIAMONDAPI *PFNALLOC)(ULONG cb); /* pfnma */ +#endif + + +/*** PFNFREE - Free memory function for MDI + * + * Entry: + * pv - Memory block allocated by matching PFNALLOC function + * + * Exit: + * Memory block freed. + */ +#ifndef _PFNFREE_DEFINED +#define _PFNFREE_DEFINED +typedef void (FAR DIAMONDAPI *PFNFREE)(MI_MEMORY pv); /* pfnmf */ +#endif + +/* --- prototypes --------------------------------------------------------- */ + +/*** MDICreateDecompression - Create MDI decompression context + * + * Entry: + * pcbDataBlockMax *largest uncompressed data block size expected, + * gets largest uncompressed data block allowed + * pfnma memory allocation function pointer + * pfnmf memory free function pointer + * pcbSrcBufferMin gets max compressed buffer size + * pmdhHandle gets newly-created context's handle + * If pmdhHandle==NULL, *pcbDataBlockMax and + * *pcbSrcBufferMin will be filled in, but no + * context will be created. This query will allow + * the caller to determine required buffer sizes + * before creating a context. + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * *pcbDataBlockMax, *pcbSrcBufferMin, *pmdhHandle filled in. + * + * Exit-Failure: + * MDI_ERROR_NOT_ENOUGH_MEMORY, could not allocate enough memory. + * MDI_ERROR_BAD_PARAMETERS, something wrong with parameters. + */ +int FAR DIAMONDAPI MDICreateDecompression( + UINT FAR * pcbDataBlockMax, /* max uncompressed data block size */ + PFNALLOC pfnma, /* Memory allocation function ptr */ + PFNFREE pfnmf, /* Memory free function ptr */ + UINT FAR * pcbSrcBufferMin, /* gets max. comp. buffer size */ + MDI_CONTEXT_HANDLE *pmdhHandle); /* gets newly-created handle */ + + +/*** MDIDecompress - Decompress a block of data + * + * Entry: + * hmd handle to decompression context + * pbSrc source buffer (compressed data) + * cbSrc compressed data size + * pbDst destination buffer (for decompressed data) + * *pcbResult decompressed data size + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * *pcbResult gets actual size of decompressed data in pbDst. + * Decompression context possibly updated. + * + * Exit-Failure: + * MDI_ERROR_BAD_PARAMETERS, something wrong with parameters. + * MDI_ERROR_BUFFER_OVERFLOW, cbDataBlockMax was too small. + */ +int FAR DIAMONDAPI MDIDecompress( + MDI_CONTEXT_HANDLE hmd, /* decompression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source data size */ + void FAR * pbDst, /* target buffer */ + UINT FAR * pcbResult); /* gets target data size */ + + +/*** MDIResetDecompression - Reset decompression history (if any) + * + * De-compression can only be started on a block which was compressed + * immediately following a MCICreateCompression() or MCIResetCompression() + * call. This function provides notification to the decompressor that the + * next compressed block begins on a compression boundary. + * + * Entry: + * hmd - handle to decompression context + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * Decompression context reset. + * + * Exit-Failure: + * Returns MDI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI MDIResetDecompression(MDI_CONTEXT_HANDLE hmd); + + +/*** MDIDestroyDecompression - Destroy MDI decompression context + * + * Entry: + * hmd - handle to decompression context + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * Decompression context destroyed. + * + * Exit-Failure: + * Returns MDI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI MDIDestroyDecompression(MDI_CONTEXT_HANDLE hmd); + +/* --- constants ---------------------------------------------------------- */ + +/* return codes */ + +#define MDI_ERROR_NO_ERROR 0 +#define MDI_ERROR_NOT_ENOUGH_MEMORY 1 +#define MDI_ERROR_BAD_PARAMETERS 2 +#define MDI_ERROR_BUFFER_OVERFLOW 3 +#define MDI_ERROR_FAILED 4 + +/* ----------------------------------------------------------------------- */ diff --git a/private/windows/diamond/mszip/mdi.nt/makefile b/private/windows/diamond/mszip/mdi.nt/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/mszip/mdi.nt/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/mszip/mdi.nt/sources b/private/windows/diamond/mszip/mdi.nt/sources new file mode 100644 index 000000000..dcf5ecd54 --- /dev/null +++ b/private/windows/diamond/mszip/mdi.nt/sources @@ -0,0 +1,37 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Ted Miller (tedm) 19-Feb-1991 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=diamond +MINORCOMP=mszip_mdi + +TARGETNAME=mdi +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +C_DEFINES=-DLARGE_STORED_BLOCKS + +SOURCES=..\mdi.c \ + ..\nfmdeco.c + +UMTYPE=console diff --git a/private/windows/diamond/mszip/nfmcomp.c b/private/windows/diamond/mszip/nfmcomp.c new file mode 100644 index 000000000..2ba5855b4 --- /dev/null +++ b/private/windows/diamond/mszip/nfmcomp.c @@ -0,0 +1,2272 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1992,1993,1994 + * All Rights Reserved. + * + * NFMCOMP.C -- memory-based compressor + * + * History: + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * normalized MCI_MEMORY type. + * 23-Feb-1994 msliger major scrub + * 24-Feb-1994 msliger Changed MCI_MEMORY to MI_MEMORY. + * 17-Mar-1994 msliger Updates for 32 bits. + * 22-Mar-1994 msliger Changed interface USHORT -> UINT. + * 06-Apr-1994 msliger Removed pack(1) for RISC; comp. bug avoided + * 12-Apr-1994 msliger Removed 1's complement from stored blocks. + * 12-May-1994 msliger ifdef'd 1's complement LARGE_STORED_BLOCKS. + * 26-Sep-1994 msliger Conserve DGROUP size in DRVSPACE app: + * Every pointer now explicitly FAR, and if + * compiled with -DDRVSPACE, larger private + * objects are created in far segments. + * 29-Sep-1994 msliger Eliminated NFM_SIG from DRVSPACE use; + * DRVSPACE has it's own signature. Cleaned + * up function declarations, trying to avoid + * "internal compiler errors". + * 16-Apr-1996 msliger Endian-independent block signature. + */ + +/* --- compilation options ------------------------------------------------ */ + +/* define for messages if put_byte overflows output buffer */ +/* #define CK_DEBUG */ + +/* --- commentary --------------------------------------------------------- */ + +/* + The "deflation" process depends on being able to identify portions + of the input text which are identical to earlier input (within a + sliding window trailing behind the input currently being processed). + + The most straightforward technique turns out to be the fastest for + most input files: try all possible matches and select the longest. + The key feature of this algorithm is that insertions into the string + dictionary are very simple and thus fast, and deletions are avoided + completely. Insertions are performed at each input character, whereas + string matches are performed only when the previous match ends. So it + is preferable to spend more time in matches to allow very fast string + insertions and avoid deletions. The matching algorithm for small + strings is inspired from that of Rabin & Karp. A brute force approach + is used to find longer strings when a small match has been found. + A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + (by Leonid Broukhis). + + A previous version of this file used a more sophisticated algorithm + (by Fiala and Greene) which is guaranteed to run in linear amortized + time, but has a larger average cost, uses more memory and is patented. + However the F&G algorithm may be faster for some highly redundant + files if the parameter max_chain_length (described below) is too large. +*/ + +/* --- preprocessor ------------------------------------------------------- */ + +#include <stdio.h> +#include <setjmp.h> +#include <string.h> /* for memcpy() */ + +#include "nfmcomp.h" + +#pragma warning(disable:4001) /* no single-line comment balking */ + +#ifndef _USHORT_DEFINED +#define _USHORT_DEFINED +typedef unsigned short USHORT; +#endif + +#ifdef DRVSPACE +#define FARBSS FAR /* put larger local objects outside DGROUP */ +#else +#define FARBSS /* no effect to other users */ +#endif + +/* --- compression-related definitions ------------------------------------ */ + +#define NFM_SIG0 'C' /* signature in a block = "CK" */ +#define NFM_SIG1 'K' +#define NFM_SIG_LEN 2 + +#ifndef WSIZE +#define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +/* Tail of hash chains */ +#define NIL 0 + +#ifdef LGM + +/* we're not hashing here, but we'll use these same defines so we don't */ +/* have to rewrite the entire program. */ + +#define HASH_BITS 16 /* the two bytes */ +#define HASH_SIZE 256 /* table size for one-byte match heads */ + +#else /* ifndef LGM: */ + +#define HASH_BITS 15 /* hash index size in bits */ +#define HASH_SIZE (USHORT)(1 << HASH_BITS) /* # entries in hash table */ +#define HASH_MASK (HASH_SIZE-1) /* mask for indexing hash */ + +#endif /* LGM */ + +#define WMASK (WSIZE-1) /* mask for indexing window */ + +/* The minimum and maximum match lengths */ +#define MIN_MATCH 3 +#define MAX_MATCH 258 + +/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + +#if (HASH_BITS < 8) || (MAX_MATCH != 258) + error: Code too clever +#endif + +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ +#define TOO_FAR 4096 + +#ifdef LGM +#define REAL_MIN 2 +#else +#define REAL_MIN (MIN_MATCH) +#endif + +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) + +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) + +/* All codes must not exceed MAX_BITS bits */ +#define MAX_BITS 15 + +/* Bit length codes must not exceed MAX_BL_BITS bits */ +#define MAX_BL_BITS 7 + +/* number of length codes, not counting the special END_BLOCK code */ +#define LENGTH_CODES 29 + +/* number of literal bytes 0..255 */ +#define LITERALS 256 + +/* end of block literal code */ +#define END_BLOCK 256 + +/* number of Literal or Length codes, including the END_BLOCK code */ +#define L_CODES (LITERALS+1+LENGTH_CODES) + +/* number of distance codes */ +#define D_CODES 30 + +/* number of codes used to transfer the bit lengths */ +#define BL_CODES 19 + +/* --- compressor definitions --------------------------------------------- */ + +#ifdef CK_DEBUG +#define put_byte(c) \ +{ \ + if (outcnt >= outsize) \ + { \ + error("compressed buffer too small"); \ + } \ + else \ + { \ + outbuf[outcnt++] = (BYTE) (c); \ + } \ +} +#else +#define put_byte(c) \ +{ \ + if (outcnt >= outsize) \ + { \ + error(""); \ + } \ + else \ + { \ + outbuf[outcnt++] = (BYTE) (c); \ + } \ +} +#endif + +/* Output a 16 bit value, lsb first */ +#define put_short(w) \ +{ \ + if (outcnt < outsize-2) \ + { \ + outbuf[outcnt++] = (BYTE) ((w) & 0xff); \ + outbuf[outcnt++] = (BYTE) ((USHORT)(w) >> 8); \ + } \ + else \ + { \ + put_byte((BYTE) ((w) & 0xff)); \ + put_byte((BYTE) ((USHORT)(w) >> 8)); \ + } \ +} + +/* + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + * Since we go from 32768 to 65536, strstart wraps around, + * so have to check it if does + */ +#define FLUSH_BLOCK(eof) \ + flush_block( \ + (block_start >= 0L) ? \ + (char FAR *) &window[(USHORT) block_start] \ + : \ + NULL, \ + (strstart == 0) ? \ + (65536L - block_start) \ + : \ + (long) strstart - block_start, \ + (eof) \ + ) + +/* + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#ifndef LGM +#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK) +#endif + +/* + * Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef LGM +#define INSERT_STRING(s, match_head) ins_str(s,&match_head) +#else +#define INSERT_STRING(s, match_head) \ + ( \ + UPDATE_HASH(ins_h, window[(s) + REAL_MIN - 1]), \ + *(prev + ((s) & WMASK)) = match_head = *(head + ins_h), \ + *(head + ins_h) = (s) \ + ) +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +/* --- local data --------------------------------------------------------- */ + +static BYTE FAR *l_buf; /* buffer for literals */ +static BYTE FAR *outbuf; /* output buffer */ +static USHORT FAR *d_buf; /* buffer for distances */ +static USHORT outcnt; /* bytes in output buffer */ +static UINT outsize; /* size of output buffer */ + +#ifdef LGM +static USHORT FAR *h1; /* one-byte match chains */ +static USHORT FAR *h2; /* two-byte match chains */ +#endif + +/* these initializations of pointers to the value 5 make them be in the */ +/* main data segment instead of the bss (uninitialized data) segment, */ +/* which is necessary for the asm code (match.asm). */ + +static BYTE FAR * window= (BYTE FAR *) 5L; /* 2*WSIZE (iow, 64K) */ + +static USHORT FAR * head= (USHORT FAR *) 5L; /* LGM: 1/2 K else: 128K */ + /* LGM: 256-entry lookup of the */ + /* head of each 1-byte chain */ + +static USHORT FAR * prev= (USHORT FAR *) 5L; /* 64K */ + /* LGM: three-byte match chains */ + +static int ins_h; /* hash index of string to be inserted */ +static int lookahead; + +static jmp_buf error_spot; + +/* + * Local data used by the "bit string" routines. + */ + +/* Output buffer. bits are inserted starting at the bottom (least significant + * bits). + */ +static USHORT bi_buf; + +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ +#define Buf_size (8 * 2*(int)sizeof(char)) + +/* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ +static int bi_valid; + +#ifdef DEBUG +ULONG bits_sent; /* bit length of the compressed data */ +#endif + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ +typedef USHORT Pos; +typedef USHORT IPos; + +static ULONG window_size = 2L * WSIZE; + +static long block_start; +/* window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + +/* Number of bits by which ins_h and del_h must be shifted at each + * input step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * H_SHIFT * MIN_MATCH >= HASH_BITS + */ +#define H_SHIFT ((HASH_BITS + MIN_MATCH - 1) / MIN_MATCH) + +/* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ +static int prev_length; + +static USHORT strstart; /* start of string to insert */ +static USHORT match_start; /* start of matching string */ + + +/* To speed up deflation, hash chains are never searched beyond this length. + * A higher limit improves compression ratio but degrades the speed. + */ +static USHORT max_chain_length = 4096; + +/* Attempt to find a better match only when the current match is strictly + * smaller than this value. + */ +static int max_lazy_match = 32; + +/* Use a faster search when the previous match is longer than this */ +static int good_match = 258; + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + +/* Stop searching when current match exceeds this: */ +static int nice_match = 258; + +/* Note: the current code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * but these restrictions can easily be removed at a small cost. + */ + +/* result of memcmp for equal strings */ +#define EQUAL 0 + +static int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +static int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +/* The three kinds of block type */ +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 + +/* comment about LIT_BUFSIZE and DIST_BUFSIZE */ +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* + * Local data + */ + +/* Data structure describing a single value and its code string. */ + +typedef struct ct_data +{ + union + { + USHORT freq; /* frequency count */ + USHORT code; /* bit string */ + } fc; + union + { + USHORT dad; /* father node in Huffman tree */ + USHORT len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +/* maximum heap size */ +#define HEAP_SIZE (2*L_CODES+1) + +static ct_data FARBSS dyn_ltree[HEAP_SIZE]; /* literal and length tree */ +static ct_data FARBSS dyn_dtree[2*D_CODES+1]; /* distance tree */ + +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ +static ct_data FARBSS static_ltree[L_CODES+2]; + +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ +static ct_data FARBSS static_dtree[D_CODES]; + +/* Huffman tree for the bit lengths */ +static ct_data FARBSS bl_tree[2*BL_CODES+1]; + +typedef struct tree_desc +{ + ct_data FAR *dyn_tree; /* the dynamic tree */ + ct_data FAR *static_tree; /* corresponding static tree or NULL */ + int FAR *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +static tree_desc l_desc = +{ + dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0 +}; + +static tree_desc d_desc = +{ + dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0 +}; + +static tree_desc bl_desc = +{ + bl_tree, (ct_data FAR *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0 +}; + + +/* number of codes at each bit length for an optimal tree */ +static USHORT FARBSS bl_count[MAX_BITS+1]; + +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ +static BYTE bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ +static int FARBSS heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ +static int heap_len; /* number of elements in the heap */ +static int heap_max; /* element of largest frequency */ + +/* Depth of each subtree used as tie breaker for trees of equal frequency */ +static BYTE FARBSS depth[2*L_CODES+1]; + +/* length code for each normalized match length (0 == MIN_MATCH) */ +static BYTE FARBSS length_code[MAX_MATCH-MIN_MATCH+1]; + +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ +static BYTE FARBSS dist_code[512]; + +/* First normalized length for each code (0 = MIN_MATCH) */ +static int FARBSS base_length[LENGTH_CODES]; + +/* First normalized distance for each code (0 = distance of 1) */ +static int FARBSS base_dist[D_CODES]; + +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ +static BYTE FARBSS flag_buf[(LIT_BUFSIZE/8)]; + +static USHORT last_lit; /* running index in l_buf */ +static USHORT last_dist; /* running index in d_buf */ +static USHORT last_flags; /* running index in flag_buf */ +static BYTE flags; /* current flags not yet saved in flag_buf */ +static BYTE flag_bit; /* current bit used in flags */ +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + +static ULONG opt_len; /* bit length of current block with optimal trees */ +static ULONG static_len; /* bit length of current block with static trees */ + +static ULONG compressed_len; /* total bit length of compressed file */ + +#ifdef DEBUG +extern ULONG bits_sent; /* bit length of the compressed data */ +#endif + +/* Send a code of the given tree. c and tree must not have side effects */ + +#ifndef DEBUG +#define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len) +#else /* DEBUG */ +#define send_code(c, tree) \ + { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(tree[c].Code, tree[c].Len); } +#endif + +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) + +/* the arguments must not have side effects */ +#define MAX(a,b) (a >= b ? a : b) + +/* --- local function prototypes ------------------------------------------ */ + +static void lm_init_clear_tables(void); +static void lm_init_use_tables(void); +static void lm_init(void); +static ULONG deflate(void); +static void ct_init(void); +static int ct_tally(int dist, int lc); +static ULONG flush_block(char FAR *buf, ULONG stored_len, int eof); +static void bi_init(void); +static void send_bits(int value, int length); +static USHORT bi_reverse(int value, int length); +static void bi_windup(void); +static void copy_block(char FAR *buf, USHORT len, int header); + +#ifdef DEBUG +static void check_match(IPos start, IPos match, int length); +#else +#define check_match(start, match, length) /* as nothing */ +#endif + +static void init_block(void); +static void pqdownheap(ct_data FAR *tree, int k); +static void gen_bitlen(tree_desc FAR *desc); +static void gen_codes(ct_data FAR *tree, int max_code); +static void build_tree(tree_desc FAR *desc); +static void scan_tree(ct_data FAR *tree, int max_code); +static void send_tree(ct_data FAR *tree, int max_code); +static int build_bl_tree(void); +static void send_all_trees(int lcodes, int dcodes, int blcodes); +static void compress_block(ct_data FAR *ltree, ct_data FAR *dtree); +static void set_file_type(void); +static int longest_match(IPos cur_match); + +#ifdef ASMV +extern void match_init(void); /* asm code initialization */ +#endif + +/* --- NFMcompress_init() ------------------------------------------------ */ + +int NFMcompress_init(void FAR *buf1,void FAR *buf2) +{ + l_buf = buf1; + d_buf = buf2; + if (!l_buf || !d_buf) + return -1; + else + return 0; +} + +/* --- NFMcompress() ----------------------------------------------------- */ + +int NFMcompress(BYTE FAR *bfSrc, UINT cbSrc, + BYTE FAR *bfDest, UINT cbDest, + MI_MEMORY bfWrk1, MI_MEMORY bfWrk2, +#ifdef LGM + MI_MEMORY bfWrk3, MI_MEMORY bfWrk4, +#endif + char fhistory, UINT FAR *pcbDestRet) +{ +#ifdef LGM + head = bfWrk1; + prev = bfWrk2; + h1 = bfWrk3; + h2 = bfWrk4; +#else + head = bfWrk2; + prev = bfWrk1; +#endif + + outbuf = bfDest; + outcnt = 0; + outsize = cbDest; + + window = bfSrc; + lookahead = cbSrc; + + if (!fhistory) + { +#ifndef BIT16 + memcpy(window+32768U,window,32768U); +#else + _fmemcpy(window+32768U,window,32768U); +#endif + } + + bi_init(); + ct_init(); + lm_init(); + + if (fhistory) + { + lm_init_use_tables(); + } + else + { + lm_init_clear_tables(); + } + + if (setjmp(error_spot) != 0) + { + return NFMinvalid; + } + +#ifndef DRVSPACE + send_bits(NFM_SIG0,8); /* put in signature */ + send_bits(NFM_SIG1,8); + + *pcbDestRet = (USHORT) (NFM_SIG_LEN + deflate()); +#else + *pcbDestRet = (USHORT) deflate(); +#endif + + if (fhistory) + { +#ifndef BIT16 + memcpy(window,window+32768U,32768U); +#else + _fmemcpy(window,window+32768U,32768U); +#endif + } + + return NFMsuccess; +} + +/* --- error() ----------------------------------------------------------- */ + +static void error(char FAR *m) +{ +#ifdef CK_DEBUG + printf("In error, got %s\n",m); +#else + m++; /* hush compiler */ +#endif + + longjmp(error_spot,-1); +} + +/* + * PURPOSE + * + * Output variable-length bit strings. Compression can be done + * to a file or to memory. (The latter is not supported in this version.) + * + * DISCUSSION + * + * The PKZIP "deflate" file format interprets compressed file data + * as a sequence of bits. Multi-bit strings in the file may cross + * byte boundaries without restriction. + * + * The first bit of each byte is the low-order bit. + * + * The routines in this file allow a variable-length bit value to + * be output right-to-left (useful for literal values). For + * left-to-right output (useful for code strings from the tree routines), + * the bits must have been reversed first with bi_reverse(). + * + * For in-memory compression, the compressed bit stream goes directly + * into the requested output buffer. The input data is read in blocks + * by the mem_read() function. The buffer is limited to 64K on 16 bit + * machines. + * + * INTERFACE + * + * void bi_init (FILE *zipfile) + * Initialize the bit string routines. + * + * void send_bits (int value, int length) + * Write out a bit string, taking the source bits right to + * left. + * + * int bi_reverse (int value, int length) + * Reverse the bits of a bit string, taking the source bits left to + * right and emitting them right to left. + * + * void bi_windup (void) + * Write out any remaining bits in an incomplete byte. + * + * void copy_block(char *buf, USHORT len, int header) + * Copy a stored block to the zip file, storing first the length and + * its one's complement if requested. + * + */ + +/* --- bi_init() --------------------------------------------------------- */ + +/* + * Initialize the bit string routines. + */ + +static void bi_init(void) +{ + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = 0L; +#endif +} + +/* --- send_bits() ------------------------------------------------------- */ + +/* + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ + +static void send_bits(int value, int length) +{ +#ifdef DEBUG + Tracev((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + bits_sent += (ULONG)length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (bi_valid > Buf_size - length) { + bi_buf |= (value << bi_valid); + put_short(bi_buf); + bi_buf = (USHORT) (value >> (Buf_size - bi_valid)); + bi_valid += length - Buf_size; + } else { + bi_buf |= value << bi_valid; + bi_valid += length; + } +} + +/* --- bi_reverse() ------------------------------------------------------ */ + +/* + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +static USHORT bi_reverse(int code, int len) +/* USHORT code; the value to invert */ +/* int len; its bit length */ +{ + register USHORT res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return ((USHORT)(res >> 1)); +} + +/* --- bi_windup() ------------------------------------------------------- */ + +/* + * Write out any remaining bits in an incomplete byte. + */ +static void bi_windup(void) +{ + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte(bi_buf); + } + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = (bits_sent+7) & ~7; +#endif +} + +/* --- copy_block() ------------------------------------------------------ */ + +/* + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +static void copy_block(char FAR *buf, USHORT len, int header) +/* char *buf; the input data */ +/* USHORT len; its length */ +/* int header; true if block header must be written */ +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + put_short((USHORT)len); +#ifdef DEBUG + bits_sent += 16; +#endif +#ifdef LARGE_STORED_BLOCKS + put_short((USHORT)~len); +#ifdef DEBUG + bits_sent += 16; +#endif +#endif + } +#ifdef DEBUG + bits_sent += (ULONG)len<<3; +#endif + while (len--) { + put_byte(*buf++); + } +} + +/* + * PURPOSE + * + * Encode various sets of source values using variable-length + * binary code trees. + * + * DISCUSSION + * + * The PKZIP "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in the ZIP file in a compressed form + * which is itself a Huffman encoding of the lengths of + * all the code strings (in ascending order by source values). + * The actual code strings are reconstructed from the lengths in + * the UNZIP process, as described in the "application note" + * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program. + * + * REFERENCES + * + * Lynch, Thomas J. + * Data Compression: Techniques and Applications, pp. 53-55. + * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7. + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + * + * INTERFACE + * + * void ct_init (USHORT *attr, int *methodp) + * Allocate the match buffer, initialize the various tables and save + * the location of the internal file attribute (ascii/binary) and + * method (DEFLATE/STORE) + * + * void ct_tally (int dist, int lc); + * Save the match info and tally the frequency counts. + * + * long flush_block (char *buf, ULONG stored_len, int eof) + * Determine the best encoding for the current block: dynamic trees, + * static trees or store, and output the encoded block to the zip + * file. Returns the total compressed length for the file so far. + * + */ + +/* + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +static void ct_init(void) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + compressed_len = 0L; + + if (static_dtree[0].Len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + length_code[length++] = (BYTE)code; + } + } + Assert (length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + length_code[length-1] = (BYTE)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + dist_code[dist++] = (BYTE)code; + } + } + Assert (dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (BYTE)code; + } + } + Assert (dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data FAR *)static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + +/* + * Initialize a new block. + */ +static void init_block(void) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) bl_tree[n].Freq = 0; + + dyn_ltree[END_BLOCK].Freq = 1; + opt_len = static_len = 0L; + last_lit = last_dist = last_flags = 0; + flags = 0; flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = heap[SMALLEST]; \ + heap[SMALLEST] = heap[heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} + +/* + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +static void pqdownheap(ct_data FAR *tree, int k) +/* ct_data *tree; the tree to restore */ +/* int k; node to move down */ +{ + int v = heap[k]; + int j = k << 1; /* left son of k */ + while (j <= heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < heap_len && smaller(tree, heap[j+1], heap[j])) j++; + + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, heap[j])) break; + + /* Exchange v with the smallest son */ + heap[k] = heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + heap[k] = v; +} + +/* + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +static void gen_bitlen(tree_desc FAR *desc) +/* tree_desc *desc; the tree descriptor */ +{ + ct_data FAR *tree = desc->dyn_tree; + int FAR *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data FAR *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + USHORT f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[heap[heap_max]].Len = 0; /* root of the heap */ + + for (h = heap_max+1; h < HEAP_SIZE; h++) { + n = heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (USHORT)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + opt_len += (ULONG)f * (bits + xbits); + if (stree) static_len += (ULONG)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (bl_count[bits] == 0) bits--; + bl_count[bits]--; /* move one leaf down the tree */ + bl_count[bits+1] += 2; /* move one overflow item as its brother */ + bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = bl_count[bits]; + while (n != 0) { + m = heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (USHORT) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + opt_len += ((long)bits-(long)tree[m].Len)*(long)tree[m].Freq; + tree[m].Len = (USHORT)bits; + } + n--; + } + } +} + +/* + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +static void gen_codes(ct_data FAR *tree, int max_code) +/* ct_data *tree; the tree to decorate */ +/* int max_code; largest code with non zero frequency */ +{ + USHORT next_code[MAX_BITS+1]; /* next code value for each bit length */ + USHORT code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (USHORT)((code + bl_count[bits-1]) << 1); + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracec(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +static void build_tree(tree_desc FAR *desc) +/* tree_desc *desc; the tree descriptor */ +{ + ct_data FAR *tree = desc->dyn_tree; + ct_data FAR *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + heap_len = 0, heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + heap[++heap_len] = max_code = n; + depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (heap_len < 2) { + int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0); + tree[new].Freq = 1; + depth[new] = 0; + opt_len--; if (stree) static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = heap_len/2; n >= 1; n--) pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = heap[SMALLEST]; /* m = node of next least frequency */ + + heap[--heap_max] = n; /* keep the nodes sorted by frequency */ + heap[--heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = (USHORT)(tree[n].Freq + tree[m].Freq); + depth[node] = (BYTE) (MAX(depth[n], depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (USHORT)node; +#ifdef DUMP_BL_TREE + if (tree == bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + heap[SMALLEST] = node++; + pqdownheap(tree, SMALLEST); + + } while (heap_len >= 2); + + heap[--heap_max] = heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc FAR *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data FAR *)tree, max_code); +} + +/* + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +static void scan_tree(ct_data FAR *tree, int max_code) +/* ct_data *tree; the tree to be scanned */ +/* int max_code; and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (USHORT)-1; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + bl_tree[curlen].Freq = (USHORT) (bl_tree[curlen].Freq + count); + } else if (curlen != 0) { + if (curlen != prevlen) bl_tree[curlen].Freq++; + bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + bl_tree[REPZ_3_10].Freq++; + } else { + bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +static void send_tree(ct_data FAR *tree, int max_code) +/* ct_data *tree; the tree to be scanned */ +/* int max_code; and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(curlen, bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(REP_3_6, bl_tree); send_bits(count-3, 2); + + } else if (count <= 10) { + send_code(REPZ_3_10, bl_tree); send_bits(count-3, 3); + + } else { + send_code(REPZ_11_138, bl_tree); send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +static int build_bl_tree(void) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree((ct_data FAR *)dyn_ltree, l_desc.max_code); + scan_tree((ct_data FAR *)dyn_dtree, d_desc.max_code); + + /* Build the bit length tree: */ + build_tree((tree_desc FAR *)(&bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len)); + + return max_blindex; +} + +/* + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +static void send_all_trees(int lcodes, int dcodes, int blcodes) +/* int lcodes, dcodes, blcodes; number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", bits_sent)); + + send_tree((ct_data FAR *)dyn_ltree, lcodes-1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", bits_sent)); + + send_tree((ct_data FAR *)dyn_dtree, dcodes-1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", bits_sent)); +} + +/* + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +static ULONG flush_block(char FAR *buf, ULONG stored_len, int eof) +/* char *buf; input block, or NULL if too old */ +/* ULONG stored_len; length of input block */ +/* int eof; true if this is the last block for a file */ +{ + ULONG opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + flag_buf[last_flags] = flags; /* Save the flags for the last 8 items */ + + /* Construct the literal and distance trees */ + build_tree((tree_desc FAR *)(&l_desc)); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len)); + + build_tree((tree_desc FAR *)(&d_desc)); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (opt_len+3+7)>>3; + static_lenb = (static_len+3+7)>>3; + if (static_lenb <= opt_lenb) + { + opt_lenb = static_lenb; + } + +#ifdef LARGE_STORED_BLOCKS + if ((stored_len+4 <= opt_lenb) && (buf != (char FAR *)0)) +#else + if ((stored_len+2 <= opt_lenb) && (buf != (char FAR *)0)) +#endif + { + /* 2 (4): one (two) word for the length */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK<<1)+eof, 3); /* send block type */ + compressed_len = (compressed_len + 3 + 7) & ~7L; +#ifdef LARGE_STORED_BLOCKS + compressed_len += (stored_len + 4) << 3; +#else + compressed_len += (stored_len + 2) << 3; +#endif + + copy_block(buf, (USHORT)stored_len, 1); /* with header */ + + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES<<1)+eof, 3); + compress_block((ct_data FAR *)static_ltree,(ct_data FAR *)static_dtree); + compressed_len += 3 + static_len; + } + else + { + send_bits((DYN_TREES<<1)+eof, 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block((ct_data FAR *)dyn_ltree, (ct_data FAR *)dyn_dtree); + compressed_len += 3 + opt_len; + } + Assert (compressed_len == bits_sent, "bad compressed size"); + init_block(); + + if (eof) + { + bi_windup(); + compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", compressed_len>>3, + compressed_len-7*eof)); + + return compressed_len >> 3; +} + +/* + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +static int ct_tally(int dist, int lc) +/* int dist; distance of matched string */ +/* int lc; match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + l_buf[last_lit++] = (BYTE)lc; + if (dist == 0) { + /* lc is the unmatched char */ + dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((USHORT)dist < (USHORT)MAX_DIST && + (USHORT)lc <= (USHORT)(MAX_MATCH-MIN_MATCH) && + (USHORT)d_code(dist) < (USHORT)D_CODES, "ct_tally: bad match"); + + dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + dyn_dtree[d_code(dist)].Freq++; + + d_buf[last_dist++] = (USHORT)dist; + flags |= flag_bit; + } + flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((last_lit & 7) == 0) { + flag_buf[last_flags++] = flags; + flags = 0, flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if ((last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ULONG out_length = (ULONG)last_lit*8L; + ULONG in_length = (ULONG)strstart-block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ULONG)dyn_dtree[dcode].Freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr,"\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + last_lit, last_dist, in_length, out_length, + 100L - out_length*100L/in_length)); + if (last_dist < last_lit/2 && out_length < in_length/2) return 1; + } + return (last_lit == LIT_BUFSIZE-1 || last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* --- compress_block() --------------------------------------------------- */ + +/* + * Send the block data compressed using the given Huffman trees + */ + +static void compress_block(ct_data FAR *ltree, ct_data FAR *dtree) +/* ct_data *ltree; literal tree */ +/* ct_data *dtree; distance tree */ +{ + int dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + USHORT lx = 0; /* running index in l_buf */ + USHORT dx = 0; /* running index in d_buf */ + USHORT fx = 0; /* running index in flag_buf */ + BYTE flag = 0; /* current flags */ + int code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (last_lit != 0) + { + do + { + if ((lx & 7) == 0) + { + flag = flag_buf[fx++]; + } + + lc = l_buf[lx++]; + + if ((flag & 1) == 0) + { + send_code(lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } + else + { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + + if (extra != 0) + { + lc -= base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + + /* Here, dist is the match distance - 1 */ + dist = d_buf[dx++]; + + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + + if (extra != 0) + { + dist -= base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + flag >>= 1; + } while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); +} + +/* --- ins_str() ---------------------------------------------------------- */ + +#ifdef LGM +/*** ins_str - Insert current string into search tables, find 3 byte match + * + * Entry: + * s - Index of string in input buffer to process (points to + * first character of string) + * match_head - Pointer to receive index of most recent previous + * 3 byte match (if any) + * Globals + * ------- + * head[] - 256 entry array, indexed by the first byte of a string, + * that points into the array h1[]. head[] records the + * start of the linked lists of 1-byte matches. + * + * h1[] - Chains together 1-byte matches. For example, if head['a'] + * is 203, then 'a' appears at position 203 in the input + * buffer, and h1[203] is next previous position in the input + * buffer than contained an 'a'. + * + * h2[] - Chains together 2-byte matches. For example, if the current + * input string is 'ab', and we looked up in head[] and followed + * the h1[] link for 'a' until we found a previous occurence of + * 'ab' in the input at position 119, then h2[119] will point + * to the *next* previous position in the input that contained + * the string 'ab'. + * + * prev[] - Chains together 3 byte matches. Following the same scheme + * as h1[] and h2[] above, if the string 'abc' is at position + * 382 in the input buffer, then prev[382] contains the next + * previous position in the input buffer where the string 'abc' + * was seen. + * + * Exit-Success: + * *match_head = valid index in input buffer previous string; + * Search tables updated for this string; + * + * Exit-Failure: + * *match_head = NIL, no previous 3 byte match found. + * Search tables updated for this string; + */ +static void ins_str(USHORT s, IPos FAR *match_head) +{ + IPos i; + BYTE b2, b3; + IPos limit = (IPos) (strstart > (IPos)MAX_DIST ? + strstart - (IPos)MAX_DIST : NIL); + USHORT chain_length = max_chain_length; /* max hash chain length */ + + ins_h = (USHORT)window[s]; /* ins_h = index of this char */ + + /** Update head of 1-byte chain and link in this byte **/ + + i = *(head+ins_h); /* i = previous occurance of this char */ + *(head+ins_h) = s; /* head[ins_h] = this occurance */ + + *(h1+(s & WMASK)) = i; /* maintain single char chain */ + + /* Follow single char chain looking for a two char match */ + + b2 = window[s+1]; /* b2 = 2nd char in string */ + while (i != NIL && b2 != window[i+1]) { + i = *(h1+(i & WMASK)); + if (i <= limit || --chain_length == 0) + i = NIL; + } + + *(h2+(s & WMASK)) = i; /* maintain two char chain */ + + /* Follow two char chain looking for a three char match */ + + b3 = window[s+2]; /* b3 = 3rd char in string */ + while (i != NIL && b3 != window[i+2]) { + i = *(h2+(i & WMASK)); + if (i <= limit || --chain_length == 0) + i = NIL; + } + + *(prev+(s & WMASK)) = i; /* maintain three char chain */ + + *match_head = i; /* return prior three char occurance */ + /* (or NIL if none) */ +} +#endif /* LGM */ + +/* --- lm_init_clear_tables() --------------------------------------------- */ + +/* + * Initialize the "longest match" routines + */ +static void lm_init_clear_tables(void) /* clear out the hash tables */ +{ + Pos j; + + /* Initialize the hash table. */ + + for (j = 0; j < HASH_SIZE; j++) + { + *(head+j) = NIL; + } + + /* prev will be initialized on the fly */ +} + +/* --- lm_init_use_tables() ----------------------------------------------- */ + +/* clear out the hash tables of 2 times ago junk */ + +static void lm_init_use_tables(void) +{ + Pos n,m; + + for (n = 0; n < HASH_SIZE; n++) + { + m = *(head+n); + *(head+n) = (Pos)((m >= WSIZE) ? (m - WSIZE) : NIL); + } + + for (n = 0; n < WSIZE; n++) + { + m = *(prev+n); + *(prev+n) = (Pos)((m >= WSIZE) ? (m - WSIZE) : NIL); + + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + +#ifdef LGM + m = *(h1+n); + *(h1+n) = (Pos)((m >= WSIZE) ? (m - WSIZE) : NIL); + + m = *(h2+n); + *(h2+n) = (Pos)((m >= WSIZE) ? (m - WSIZE) : NIL); +#endif + } +} + +/* --- lm_init() ---------------------------------------------------------- */ + +static void lm_init(void) +{ +#ifndef LGM + USHORT j; +#endif + + strstart = 32768U; + block_start = 32768L; + + /* lookahead already set */ + +#ifdef ASMV + match_init(); /* asm code initialization */ +#endif + + ins_h = 0; + +#ifndef LGM + for (j = 0; j < (REAL_MIN - 1); j++) + { + UPDATE_HASH(ins_h, window[j + strstart]); + } + + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +#endif +} + +/* --- longest_match() ---------------------------------------------------- */ + +/* + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ + +/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or + * match.s. The code is functionally equivalent, so you can use the C version + * if desired. + */ +#ifndef ASMV +static int longest_match(IPos cur_match) +{ + USHORT chain_length = max_chain_length; /* max hash chain length */ + register BYTE FAR *scan = window + strstart; /* current string */ + register BYTE FAR *match; /* matched string */ + register int len; /* length of current match */ + int best_len = prev_length; /* best match length so far */ + IPos limit; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register BYTE FAR *strend = window + strstart + MAX_MATCH - 1; + register USHORT scan_start = *(USHORT FAR *)scan; + register USHORT scan_end = *(USHORT FAR *)(scan + best_len - 1); +#else + register BYTE FAR *strend = window + strstart + MAX_MATCH; + register BYTE scan_end1 = scan[best_len - 1]; + register BYTE scan_end = scan[best_len]; +#endif + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + if (strstart > (IPos)MAX_DIST) + { + limit = (IPos)(strstart - MAX_DIST); + } + else + { + limit = NIL; + } + +//BUGBUG 01-Mar-1994 msliger What's this doing? + /* Do not waste too much time if we already have a good match: */ + if (prev_length >= good_match) + { + chain_length >>= 2; + } + + Assert(strstart <= window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do + { + Assert(cur_match < strstart, "no future"); + match = window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + + /* This code assumes sizeof(USHORT) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + + if ((*(USHORT FAR *)(match + best_len - 1) != scan_end) || + **(USHORT FAR *)match != scan_start)) + { + continue; + } + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If (MAX_MATCH - 2) is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + scan++; + match++; + + do + { + } while ((*(USHORT FAR *)(scan += 2) == *(USHORT FAR *)(match += 2)) && + (*(USHORT FAR *)(scan += 2) == *(USHORT FAR *)(match += 2)) && + (*(USHORT FAR *)(scan += 2) == *(USHORT FAR *)(match += 2)) && + (*(USHORT FAR *)(scan += 2) == *(USHORT FAR *)(match += 2)) && + (scan < strend)); + + /* Here, scan <= window+strstart+257 */ + + Assert(scan <= window+(USHORT)(window_size-1), "wild scan"); + + if (*scan == *match) + { + scan++; + } + + len = (USHORT) (scan - strend + (MAX_MATCH-1)); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if ((match[best_len] != scan_end) || + (match[best_len-1] != scan_end1) || (*match != *scan) || + (*++match != scan[1]) || (*++match != scan[2])) + { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + + /* duh you idiot whoever wrote this code. MIN_MATCH is a defined */ + /* constant that can be changed, but if you do it breaks THIS */ + /* shitty code. So now hack away using REAL_MIN. */ + + scan += MIN_MATCH - 1; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + + do + { + } while ((*++scan == *++match && *++scan == *++match) && + (*++scan == *++match && *++scan == *++match) && + (*++scan == *++match && *++scan == *++match) && + (*++scan == *++match && *++scan == *++match) && + (scan < strend)); + + len = scan - strend + MAX_MATCH; + + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) + { + match_start = cur_match; + best_len = len; + + if (len >= nice_match) + { + break; + } + +#ifdef UNALIGNED_OK + scan_end = *(USHORT FAR *)(scan + best_len - 1); +#else + scan_end1 = scan[best_len - 1]; + scan_end = scan[best_len]; +#endif + } + } while (((cur_match = *(prev+(cur_match & WMASK))) > limit) && + (--chain_length != 0)); + + /* if (chain_length == 0) */ + /* printf("Out of chain length\n"); */ + + return best_len; +} +#endif /* ASMV */ + +/* --- check_match() ------------------------------------------------------ */ + +#ifdef DEBUG +/* + * Check that the match at match_start is indeed a match. + */ +static void check_match(IPos start, IPos match, int length) +{ + /* check that the match is indeed a match */ + + if (_fmemcmp((char FAR *) window + match, + (char FAR *) window + start, length) != EQUAL) + { + error("invalid match"); + } +} +#endif + +/* --- deflate() --------------------------------------------------------- */ + +/* + * Processes a new input file and return its compressed length. + */ + +#ifdef NO_LAZY + +static ULONG deflate(void) +{ + IPos hash_head; /* head of the hash chain */ + int flush; /* set if current block must be flushed */ + USHORT match_length = 0; /* length of best match */ + + prev_length = MIN_MATCH - 1; + + while (lookahead != 0) + { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (lookahead > REAL_MIN-1) + { + INSERT_STRING(strstart, hash_head); + } + else + { /* make it do a literal, not adding to hash trees */ + hash_head = NIL; + match_length = 0; + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + + if ((hash_head != NIL) && (strstart - hash_head <= MAX_DIST) && + (strstart < 65533)) + { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + + match_length = longest_match (hash_head); + + /* longest_match() sets match_start */ + + if (match_length > lookahead) + { + match_length = lookahead; + } + } + + if (match_length >= MIN_MATCH) + { + check_match(strstart, match_start, match_length); + + flush = ct_tally(strstart-match_start, match_length - MIN_MATCH); + + lookahead -= match_length; + match_length--; /* string at strstart already in hash table */ + + do + { + strstart++; + + if (lookahead > REAL_MIN-1) + { + INSERT_STRING(strstart, hash_head); + } + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--match_length != 0); + } + else + { + /* No match, output a literal byte */ + + flush = ct_tally (0, window[strstart]); + lookahead--; + } + + strstart++; + + if (flush) + { + FLUSH_BLOCK(0); + block_start = strstart; + } + } + + return FLUSH_BLOCK(1); /* eof */ +} + +#else /* LAZY */ + +/* + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +static ULONG deflate(void) +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register int match_length; /* length of best match */ + + match_length = MIN_MATCH - 1; + + /* Process the input block */ + + while (lookahead != 0) + { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (lookahead > (REAL_MIN - 1)) + { + INSERT_STRING(strstart, hash_head); + } + else /* make it do a literal, not adding to hash trees */ + { + hash_head = NIL; + prev_length = 0; + } + + /* Find the longest match, discarding those <= prev_length */ + + prev_length = match_length; + prev_match = match_start; + match_length = MIN_MATCH - 1; + + if ((hash_head != NIL) && (prev_length < max_lazy_match) && + (strstart - hash_head <= MAX_DIST) && (strstart < 65533)) + { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + + match_length = longest_match (hash_head); + /* longest_match() sets match_start */ + + if (match_length > lookahead) + { + match_length = lookahead; + } + + /* Ignore a length 3 match if it is too distant: */ + if ((match_length == MIN_MATCH) && + ((strstart - match_start) > TOO_FAR)) + { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + + /* If there was a match at the previous step and the current + * match is not better, output the previous match: */ + + if ((prev_length >= MIN_MATCH) && (match_length <= prev_length)) + { + check_match(strstart-1, prev_match, prev_length); + + flush = ct_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + + lookahead -= prev_length-1; + prev_length -= 2; + + do + { + strstart++; + + if (lookahead > REAL_MIN-1) + { + INSERT_STRING(strstart, hash_head); + } + + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--prev_length != 0); + + match_available = 0; + match_length = MIN_MATCH-1; + strstart++; + if (flush) + { + FLUSH_BLOCK(0); + block_start = strstart; + } + } + else if (match_available) + { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c",window[(USHORT) (strstart-1)])); + + if (ct_tally (0, window[(USHORT) (strstart-1)])) + { + FLUSH_BLOCK(0); + block_start = strstart; + } + + strstart++; + lookahead--; + } + else + { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + strstart++; + lookahead--; + } + } + + if (match_available) + { + ct_tally(0, window[(USHORT) (strstart-1)]); + } + + return FLUSH_BLOCK(1); /* eof */ +} +#endif /* LAZY */ + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/mszip/nfmcomp.h b/private/windows/diamond/mszip/nfmcomp.h new file mode 100644 index 000000000..e8512e34d --- /dev/null +++ b/private/windows/diamond/mszip/nfmcomp.h @@ -0,0 +1,103 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993,1994 + * All Rights Reserved. + * + * NFMCOMP.H -- definitions for NFMCOMP.C + * + * History: + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * normalized MCI_MEMORY type. + * 23-Feb-1994 msliger changed how LGM defines work + * 24-Feb-1994 msliger Changed MCI_MEMORY to MI_MEMORY. + * 15-Mar-1994 msliger Changes for 32 bits. + * 22-Mar-1994 msliger Changed !INT32 to BIT16. + * Changed interface USHORT -> UINT. + * 12-May-1994 msliger ifdef'd 1's complement LARGE_STORED_BLOCKS. + * 26-Sep-1994 msliger Changed pcbResult to a FAR pointer. + * 12-Jun-1995 msliger Found cases to increase MAX_GROWTH. + */ + +#ifndef NO_LGM +#define LGM +#endif + +/* --- types -------------------------------------------------------------- */ + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif + +#ifndef _UINT_DEFINED +#define _UINT_DEFINED +typedef unsigned int UINT; +#endif + +#ifndef _ULONG_DEFINED +#define _ULONG_DEFINED +typedef unsigned long ULONG; +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE huge +#else +#define HUGE +#endif +#endif + +#ifndef _MI_MEMORY_DEFINED +#define _MI_MEMORY_DEFINED +typedef void HUGE * MI_MEMORY; +#endif + +/* --- warnings ----------------------------------------------------------- */ + +/* BUGBUG There is a known problem where the compressor may become unstable + * if cbDest is too small. (last_lit will not be reset, amongst + * others.) If the current block fails because it would exceed + * cbDest, additional blocks are likely to be mis-compresssed, and + * likely to fault in the compressor. + */ + +/* --- prototypes --------------------------------------------------------- */ + +extern int NFMcompress_init(MI_MEMORY buf1,MI_MEMORY buf2); + +extern int NFMcompress(BYTE FAR *bfSrc, UINT cbSrc, + BYTE FAR *bfDest, UINT cbDest, + MI_MEMORY bfWrk1, MI_MEMORY bfWrk2, +#ifdef LGM + MI_MEMORY bfWrk3, MI_MEMORY bfWrk4, +#endif + char fhistory, UINT FAR *pcbDestRet); + +/* --- constants ---------------------------------------------------------- */ + +/* return codes */ + +#define NFMsuccess 0 /* successful completion */ +#define NFMdestsmall 1 /* destination buffer is too small */ +#define NFMoutofmem 2 /* alloc returned an error (NULL) */ +#define NFMinvalid 3 /* source buffer invalid for operation */ + + +#ifdef LARGE_STORED_BLOCKS +#define MAX_GROWTH 12 /* maximum growth of a block */ +#else +#define MAX_GROWTH 8 /* maximum growth of a block */ +#endif + +#define LIT_BUFSIZE 0x8000 /* literal buffer */ +#define DIST_BUFSIZE 0x8000 /* distance buffer */ + +/* ----------------------------------------------------------------------- */ diff --git a/private/windows/diamond/mszip/nfmdeco.c b/private/windows/diamond/mszip/nfmdeco.c new file mode 100644 index 000000000..93ceb0e27 --- /dev/null +++ b/private/windows/diamond/mszip/nfmdeco.c @@ -0,0 +1,1721 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1992,1993,1994,1995 + * All Rights Reserved. + * + * NFMDECO.C -- memory-based decompressor + * + * History: + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * normalized MCI_MEMORY type. + * 24-Feb-1994 msliger Changed MDI_MEMORY to MI_MEMORY. + * 17-Mar-1994 msliger Updates for 32 bits. + * 22-Mar-1994 msliger Initial work to speed up. + * 31-Mar-1994 msliger Changed to private setjmp/longjmp. + * 06-Apr-1994 msliger Removed pack(1) for RISCs, added UNALIGNED + * 12-Apr-1994 msliger Eliminated setjmp/longjmp. Optimized + * stored blocks. + * 13-Apr-1994 msliger Defined call convention for alloc/free. + * 12-May-1994 msliger ifdef'd 1's complement LARGE_STORED_BLOCKS + * 07-Oct-1994 msliger Numerous opts & API enhancements getting + * ready for ASM port. + * 15-Nov-1994 msliger Update source ptr during stored blocks. + * Reduced internal tables to anticipated + * sizes. Reset IncrementalState after a + * resume during a stored block. Make sure + * global bit buffer is empty after a stored + * block. Removed refs to NFMalloc, NFMfree. + * 12-Mar-1995 msliger Enhanced DISPLAY_DECO output. + * 25-May-1995 msliger Dropped NFMuncompress, added NFM_Prepare() + * and NFM_Decompress(). + * 16-Apr-1996 msliger Endian-independent block signature check. + */ + +/* --- compilation options ------------------------------------------------ */ + +/* #define DISPLAY */ /* enables huf info dumping (BROKEN 'cuz we don't know tree size now) */ +/* #define DISPLAY_DECO */ /* enables decompression dumping */ +/* #define CK_DEBUG */ /* turns on error reporting */ + +#pragma intrinsic(memcpy) + +/* --- preprocessor ------------------------------------------------------- */ + +#include <stdio.h> /* for NULL */ +#include <string.h> /* for memset() */ + +#include "diamondd.h" /* to get PFNALLOC, PFNFREE types */ +#include "nfmdeco.h" /* prototype verification */ + +#pragma warning(disable:4001) /* no single-line comment balking */ + +#ifndef _USHORT_DEFINED +#define _USHORT_DEFINED +typedef unsigned short USHORT; +#endif + +#ifndef NEAR +#ifdef BIT16 +#define NEAR near +#else +#define NEAR +#endif +#endif + +//typedef unsigned short BOOL; +//#define TRUE 1 +//#define FALSE 0 + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). + Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 + means that v is a literal, 16 < e < 32 means that v is a pointer to + the next table, which codes e - 16 bits, and lastly e == 99 indicates + an unused code. If a code with e == 99 is looked up, this implies an + error in the data. */ + +/* If POINTERS is defined, the tree is built using direct pointers + Otherwise, the pointers in the tree are offsets from the beginning + of the tree. Offsets are space-efficient on a 32-bit implementation; + pointers may be very slightly faster on the 16-bit implementation. */ + +#define POINTERS 1 + +typedef struct huft +{ + BYTE e; /* number of extra bits or operation */ + BYTE b; /* number of bits in this code or subcode */ + union + { + USHORT n; /* literal, length base, or distance base */ +#ifdef POINTERS + struct huft *t; /* pointer to next level of table */ +#else + USHORT t; /* 'pointer' to next table as offset in current table */ +#endif + } v; +} HUFF_TREE; + +/* --- commentary --------------------------------------------------------- */ + +/* + Inflate deflated (PKZIP's method 8 compressed) data. The compression + method searches for as much of the current string of bytes (up to a + length of 258) in the previous 32K bytes. If it doesn't find any + matches (of at least length 3), it codes the next byte. Otherwise, it + codes the length of the matched string and its distance backwards from + the current position. There is a single Huffman code that codes both + single bytes (called "literals") and match lengths. A second Huffman + code codes the distance information, which follows a length code. Each + length or distance code actually represents a base value and a number + of "extra" (sometimes zero) bits to get to add to the base value. At + the end of each deflated block is a special end-of-block (EOB) literal/ + length code. The decoding process is basically: get a literal/length + code; if EOB then done; if a literal, emit the decoded byte; if a + length then get the distance and emit the referred-to bytes from the + sliding window of previously emitted data. + + There are (currently) three kinds of inflate blocks: stored, fixed, and + dynamic. The compressor deals with some chunk of data at a time, and + decides which method to use on a chunk-by-chunk basis. A chunk might + typically be 32K or 64K. If the chunk is uncompressible, then the + "stored" method is used. In this case, the bytes are simply stored as + is, eight bits per byte, with none of the above coding. The bytes are + preceded by a count, since there is no longer an EOB code. + + If the data is compressible, then either the fixed or dynamic methods + are used. In the dynamic method, the compressed data is preceded by + an encoding of the literal/length and distance Huffman codes that are + to be used to decode this block. The representation is itself Huffman + coded, and so is preceded by a description of that code. These code + descriptions take up a little space, and so for small blocks, there is + a predefined set of codes, called the fixed codes. The fixed method is + used if the block codes up smaller that way (usually for quite small + chunks), otherwise the dynamic method is used. In the latter case, the + codes are customized to the probabilities in the current block, and so + can code it much better than the pre-determined fixed codes. + + The Huffman codes themselves are decoded using a mutli-level table + lookup, in order to maximize the speed of decoding plus the speed of + building the decoding tables. See the comments below that precede the + LBITS and DBITS tuning parameters. + + + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables LBITS and DBITS + below. LBITS is the number of bits the first level table for literal/ + length codes can decode in one step, and DBITS is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so LBITS is 8+1 and DBITS is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + + + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + + The inflate algorithm uses a sliding 32K byte window on the uncompressed + stream to find repeated byte strings. This is implemented here as a + circular buffer. The index is updated simply by incrementing and then + and'ing with 0x7fff (32K-1). This buffer is the uncompressed data output + buffer. When subsequent blocks are presented to be decompressed, the + caller must return the buffer to Inflate() with the result of the last + decompression still intact. + + + Macros for Inflate() bit peeking and grabbing. + The usage is: + + NEEDBITS(j) + x = b & mask_bits[j]; + DUMPBITS(j) + + where NEEDBITS makes sure that b has at least j bits in it, and + DUMPBITS removes the bits from b. The macros use the variable k + for the number of bits in b. Normally, b and k are register + variables for speed, and are initialized at the begining of a + routine that uses these macros from a global bit buffer and count. + + If we assume that EOB will be the longest code, then we will never + ask for bits with NEEDBITS that are beyond the end of the stream. + So, NEEDBITS should not read any more bytes than are needed to + meet the request. Then no bytes need to be "returned" to the buffer + at the end of the last block. + + However, this assumption is not true for fixed blocks--the EOB code + is 7 bits, but the other literal/length codes can be 8 or 9 bits. + (The EOB code is shorter than other codes becuase fixed blocks are + generally short. So, while a block always has an EOB, many other + literal/length codes have a significantly lower probability of + showing up at all.) However, by making the first table have a + lookup of seven bits, the EOB code will be found in that first + lookup, and so will not require that too many bits be pulled from + the stream. +*/ + +/* --- state data --------------------------------------------------------- */ + +#define sNEWBLOCK 0 /* At beginning of new block */ +#define sSTORE 1 /* Store operation in progress */ +#define sHUFFTREE 2 /* Entering new match/ literal */ +#define sDONE 3 /* Completed block, no data left */ + +#define FIX_HTL_SIZE (520) /* determined by observation */ +#define FIX_HTD_SIZE (32) /* determined by observation */ + +/* BUGBUG determine real max size required for these trees */ +#define HTL_SIZE (800) /* have seen up to 778 */ +#define HTD_SIZE (150) /* have seen up to 140 */ + + +/* --- local data --------------------------------------------------------- */ + +typedef struct _MSZIP_DECOMPRESS_CONTEXT { + + int lastBlock; /* set 1 when last block has been read */ + int get_error; /* flag set if we over-run input buffer */ + + BYTE * inbuf; /* input buffer */ + BYTE * outbuffer; /* output pointer */ + BYTE * outstart; /* beginning of buffer - needed for match copies */ + + unsigned insize; /* valid bytes in inbuf */ + unsigned inptr; /* index of next byte to process in inbuf */ + + unsigned outleft; /* bytes remaining in output buffer request */ + unsigned bufavail; /* bytes available in output buffer */ + + ULONG bb; /* the global bit buffer */ + unsigned bk; /* number of bits in global bit buffer */ + + /* function pointers to external memory allocator/deallocator */ + + PFNALLOC nfm_malloc; + PFNFREE nfm_free; + + int IncrementalState; + + unsigned sstoreNBytes; /* for STORE, number of bytes remaining in block */ + BYTE *sstorePointer; /* for STORE, pointer to source buffer */ + + HUFF_TREE * streeTL; /* for HUFFTREE, saved literal table */ + HUFF_TREE * streeTD; /* for HUFFTREE, saved distance table */ + int streeBL; /* for HUFFTREE, saved bits in TL */ + int streeBD; /* for HUFFTREE, saved bits in TD */ + BOOL streeIsMatch; /* for HUFFTREE, saved is TRUE if match in progress */ + BYTE * streePointer; /* for HUFFTREE MATCH, pointer to source bytes */ + unsigned streeNumber; /* for HUFFTREE MATCH, number of bytes remaining */ + + int fixed_init; + + HUFF_TREE fhtl[FIX_HTL_SIZE]; + HUFF_TREE fhtd[FIX_HTD_SIZE]; + + /* Note: these initial values may be changed when */ + /* the static trees are built */ + int fixedblit; /* number of bits in literal tree */ + int fixedbdist; /* number of bits in distance tree */ + + HUFF_TREE htl[HTL_SIZE]; /* the huffman tree for literals */ + HUFF_TREE htd[HTD_SIZE]; /* the huffman tree for distances */ + +} MSZIP_DECOMPRESS_CONTEXT, *PMSZIP_DECOMPRESS_CONTEXT; + +/* --- compression-related definitions ------------------------------------ */ + +#define NFM_SIG0 'C' /* signature in a block = "CK" */ +#define NFM_SIG1 'K' +#define NFM_SIG_LEN 2 + +#ifndef WSIZE +#define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define STORED 0 /* block is simply stored */ +#define FIXED 1 /* block uses the fixed tree */ +#define DYNAMIC 2 /* block uses a dynamic tree */ + +#define LBITS 9 /* bits in base literal/length lookup table */ +#define DBITS 6 /* bits in base distance lookup table */ + +/* If BMAX needs to be larger than 16, then h and x[] should be ULONG. */ + +#define BMAX 16 /* max. bit length of any code (16 for explode) */ +#define N_MAX 288 /* maximum number of codes in any set */ + +/* Tables for deflate from PKZIP's appnote.txt. */ + +static unsigned border[] = /* Order of the bit length code lengths */ +{ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +static USHORT cplens[] = /* Copy lengths for literal codes 257..285 */ +{ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +static USHORT cplext[] = /* Extra bits for literal codes 257..285 */ +{ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 +}; /* 99 -> invalid */ + +static USHORT cpdist[] = /* Copy offsets for distance codes 0..29 */ +{ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 +}; + +static USHORT cpdext[] = /* Extra bits for distance codes */ +{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static USHORT mask_bits[] = /* masks to get # bits from a value */ +{ + 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, + 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, + 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF +}; + +/* --- decompressor definitions ------------------------------------------- */ + +/* BUGBUG 05-Jun-95 MSliger This function will never fail: doesn't advance */ + +/* get_char() retrieves the next character from the input buffer. If the + input pointer extends beyond the buffer size, the error flag is set, and + zero is returned. Reading only one byte beyond the buffer does not set + the error flag because inflate() could pre-fetch too far while grabbing + the EOB code. We still don't try to fetch the over-shot byte from the + buffer because that could cause a protection fault. */ + +/* if this was a function, it would read: +BYTE get_char() +{ + if (Context->inptr < Context->insize) + { + return(Context->inbuf[Context->inptr++]); + } + else + { + if (Context->inptr == Context->insize) + { + return(0); + } + else + { + Context->get_error = 1; + return(0); + } + } +} + +but it's a macro, so it reads: */ + +#define get_char() \ +(BYTE) \ +( \ + (Context->inptr < Context->insize) ? \ + ( \ + Context->inbuf[Context->inptr++] \ + ) \ + : \ + ( \ + (Context->inptr == Context->insize) ? \ + ( \ + 0 \ + ) \ + : \ + ( \ + Context->get_error = 1, \ + 0 \ + ) \ + ) \ +) + +#define NEEDBITS(n) \ + { \ + while (k < (n)) \ + { \ + b |= ((ULONG) get_char()) << k; \ + k += 8; \ + } \ + } + +#define DUMPBITS(n) \ + { \ + b >>= (n); \ + k -= (n); \ + } + +#define MASK1(x) ((int) (x & 0x0001)) +#define MASK2(x) ((int) (x & 0x0003)) +#define MASK3(x) ((int) (x & 0x0007)) +#define MASK4(x) ((int) (x & 0x000F)) +#define MASK5(x) ((int) (x & 0x001F)) +#define MASK6(x) ((int) (x & 0x003F)) +#define MASK7(x) ((int) (x & 0x007F)) + +/* --- local function prototypes ------------------------------------------ */ + +static int NEAR HuftBuild(PMSZIP_DECOMPRESS_CONTEXT, + unsigned *, unsigned, unsigned, USHORT *, USHORT *, + HUFF_TREE *, unsigned, int *); +static int NEAR InflateCodes(PMSZIP_DECOMPRESS_CONTEXT,HUFF_TREE *, HUFF_TREE *, int, int, BOOL); +static int NEAR InflateStored(PMSZIP_DECOMPRESS_CONTEXT,BOOL); +static int NEAR InflateFixed(PMSZIP_DECOMPRESS_CONTEXT); +static int NEAR InflateDynamic(PMSZIP_DECOMPRESS_CONTEXT); +static int NEAR InflateBlock(PMSZIP_DECOMPRESS_CONTEXT); +static int NEAR InitFixed(PMSZIP_DECOMPRESS_CONTEXT); + +#ifdef DISPLAY +static void NEAR DisplayTree(HUFF_TREE *); +#endif + +/* --- HuftBuild() -------------------------------------------------------- */ + +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. */ + +/* unsigned *b; code lengths in bits (all assumed <= BMAX) */ +/* unsigned n; number of codes (assumed <= N_MAX) */ +/* unsigned s; number of simple-valued codes (0..s-1) */ +/* USHORT *d; list of base values for non-simple codes */ +/* USHORT *e; list of extra bits for non-simple codes */ + +/* HUFF_TREE *ht the array in which to build tree */ +/* int hts the size of the ht array */ + +/* int *m; maximum lookup bits, returns actual */ + +static int NEAR HuftBuild(PMSZIP_DECOMPRESS_CONTEXT Context, + unsigned *b, unsigned n, unsigned s, USHORT *d, + USHORT *e, HUFF_TREE *ht, unsigned htmax, int *m) +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX+1]; /* bit length count table */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register unsigned i; /* counter, current code */ + register unsigned j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register unsigned *p; /* pointer into c[], b[], or v[] */ +#ifdef POINTERS + register HUFF_TREE *q; /* points to current table */ + HUFF_TREE *u[BMAX]; /* table stack */ +#else + unsigned q = 0; /* offset to current table within current block */ + unsigned u[BMAX]; /* table stack */ +#endif + HUFF_TREE r; /* table entry for structure assignment */ + unsigned v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + unsigned x[BMAX+1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + + unsigned z; /* number of entries in current table */ + unsigned htnext = 0; /* next free entry in ht */ + +#ifdef DISPLAY + memset(&r,0,sizeof(r)); /* tidy up */ + memset(v,0,sizeof(v)); + memset(x,0,sizeof(x)); +#endif + + memset(c, 0, sizeof(c)); /* Generate counts for each bit len */ + p = b; + i = n; + + do + { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + + if (c[0] == n) /* null input: all 0-length codes */ + { + *m = 0; + return 0; + } + + l = *m; /* Find min/max length, bound *m */ + for (j = 1; j <= BMAX; j++) + { + if (c[j]) + { + break; + } + } + + k = j; /* minimum code length */ + + if ((unsigned) l < j) + { + l = j; + } + + for (i = BMAX; i; i--) + { + if (c[i]) + { + break; + } + } + + g = i; /* maximum code length */ + + if ((unsigned) l > i) + { + l = i; + } + + *m = l; + + /* Adjust last length count to fill out codes, if needed */ + + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { +#ifdef CK_DEBUG +printf("HUFT_BUILD: bad 1\n"); +#endif + return 2; /* bad input: more codes than bits */ + } + } + + if ((y -= c[i]) < 0) + { +#ifdef CK_DEBUG +printf("HUFT_BUILD: bad 2\n"); +#endif + return 2; + } + + c[i] += y; + + /* Generate starting offsets into the value table for each length */ + + x[1] = j = 0; + + p = c + 1; + + xp = x + 2; + + while (--i) + { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + /* Make a table of values in order of bit lengths */ + + p = b; + + i = 0; + + do + { + if ((j = *p++) != 0) + { + v[x[j]++] = i; + } + } while (++i < n); + + /* Generate the Huffman codes and for each, make the table entries */ + + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ +#ifdef POINTERS + u[0] = NULL; + q = NULL; +#else + u[0] = (unsigned) -1; + q = 0; +#endif + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + + for (; k <= g; k++) + { + a = c[k]; + + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + + /* upper limit on table size */ + + z = (USHORT) g - w; + + if (z > (unsigned) l) + { + z = (USHORT) l; + } + + /* try a k-w bit table */ + + if ((f = 1 << (j = k - w)) > a + 1) + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + { + break; /* enough codes to use up j bits */ + } + + f -= *xp; /* else deduct codes from patterns */ + } + } + + z = (USHORT) 1 << j; /* table entries for j-bit table */ + +#ifdef POINTERS + q = ht + htnext; +#else + q = htnext; +#endif + htnext += z; + if (htnext > htmax) + { +#ifdef CK_DEBUG +printf("[not enough memory]"); +#endif + return 3; /* not enough memory */ + } + +#ifdef DISPLAY +#ifdef POINTERS + memset(q, 0, z * sizeof(HUFF_TREE)); /* tidy up */ +#else + memset(ht + q, 0, z * sizeof(HUFF_TREE)); /* tidy up */ +#endif + +/* Here we used to set number of nodes (z) into first entry (link) + but now we aren't keeping the extra entry. */ + +#endif + + u[h] = q; + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.b = (BYTE)l; /* bits to dump before this table */ + r.e = (BYTE)(16 + j); /* bits in this table */ +#ifdef POINTERS + r.v.t = q; + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ +#else + r.v.t = (USHORT) q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + *(ht + u[h-1] + j) = r; /* connect to last table */ +#endif + } + } + + /* set up table entry in r */ + + r.b = (BYTE)(k - w); + + if (p >= v + n) + { + r.e = 99; /* out of values--invalid code */ + } + else if (*p < s) + { /* 256 is end-of-block code */ + r.e = (BYTE)(*p < 256 ? 16 : 15); + r.v.n = (USHORT) *p++; /* simple code is just the value */ + } + else + { + r.e = (BYTE)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = (USHORT) d[*p++ - s]; + } + + /* fill code-like entries with r */ + + f = 1 << (k - w); + + for (j = i >> w; j < z; j += f) + { +#ifdef POINTERS + q[j] = r; +#else + *(ht + q + j) = r; +#endif + } + + /* backwards increment the k-bit code i */ + + for (j = 1 << (k - 1); i & j; j >>= 1) + { + i ^= j; + } + + i ^= j; + + /* backup over finished tables */ + + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + /* Return true (1) if we were given an incomplete table */ + +#ifdef DISPLAY +#ifdef POINTERS + DisplayTree(u[0]); /* display finished results */ +#else + DisplayTree(ht); /* display finished results */ +#endif +#endif + + return ((y != 0) && (g != 1)); +} + +/* --- InflateCodes() ----------------------------------------------------- */ + +/* HUFF_TREE *tl, *td; literal/length and distance decoder tables */ +/* int bl, bd; number of bits decoded by tl[] and td[] */ +/* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ + +static int NEAR InflateCodes(PMSZIP_DECOMPRESS_CONTEXT Context,HUFF_TREE *tl, HUFF_TREE *td, int bl, int bd, BOOL bResume) +{ + register unsigned e; /* table entry flag/number of extra bits */ + unsigned n, d; /* length and index for copy */ + HUFF_TREE *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register ULONG b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + BYTE * dptr; /* pointer used for match/copy */ + + /* make local copies of globals */ + b = Context->bb; /* initialize bit buffer */ + k = Context->bk; + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + + + /* Resuming a previous inflate in the middle of a match? */ + if (bResume && Context->streeIsMatch) + { + dptr = Context->streePointer; + n = Context->streeNumber; + goto ResumeMatch; + } + + + for (;;) /* do until end of block */ + { + NEEDBITS((unsigned)bl) + if (Context->get_error) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 1\n"); +#endif + return(1); + } + + if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) + { + do + { + if (e == 99) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 2\n"); +#endif + return 1; + } + +#ifdef DISPLAY + if (t->b) + { + printf("%d ",b & mask_bits[t->b]); + } +#endif + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + if (Context->get_error) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 3\n"); +#endif + return(1); + } +#ifdef POINTERS + } while ((e = (t = t->v.t + + ((unsigned)b & mask_bits[e]))->e) > 16); +#else + } while ((e = (t = (tl + t->v.t) + + ((unsigned)b & mask_bits[e]))->e) > 16); +#endif + } +#ifdef DISPLAY + if (t->b) + { + printf("%d ",b & mask_bits[t->b]); + } +#endif + DUMPBITS(t->b) + + if (e == 16) /* then it's a literal */ + { +#ifdef DISPLAY_DECO + if ((t->v.n >= ' ') && (t->v.n <= '~')) + { + printf("%5ld %5ld: '%c'\n",(unsigned long) Context->inptr, + (unsigned long) (Context->outbuffer - Context->outstart),t->v.n); + } + else + { + printf("%5ld %5ld: 0x%02X\n",(unsigned long) Context->inptr, + (unsigned long) (Context->outbuffer - Context->outstart),t->v.n); + } +#endif + *Context->outbuffer++ = ((BYTE)t->v.n); /* Emit one literal byte */ + if (--Context->outleft == 0) /* More output bytes requested? */ + { + Context->IncrementalState = sHUFFTREE; /* NO, store state for restart */ + Context->streeIsMatch = FALSE; + Context->streeTL = tl; + Context->streeTD = td; + Context->streeBL = bl; + Context->streeBD = bd; + goto Done; + } + } + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if (e == 15) + { + break; + } + + /* get length of block to copy */ + NEEDBITS(e) + if (Context->get_error) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 4\n"); +#endif + return(1); + } + + n = t->v.n + ((unsigned)b & mask_bits[e]); +#ifdef DISPLAY + if (e) + { + printf("%d ",b & mask_bits[e]); + } +#endif + DUMPBITS(e); + + /* decode distance of block to copy */ + NEEDBITS((unsigned)bd) + if (Context->get_error) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 5\n"); +#endif + return(1); + } + + if ((e = (t = td + ((unsigned)b & md))->e) > 16) + { + do + { + if (e == 99) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 6\n"); +#endif + return 1; + } +#ifdef DISPLAY + if (t->b) + { + printf("%d ",b & mask_bits[t->b]); + } +#endif + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + if (Context->get_error) + { + return(1); + } + +#ifdef POINTERS + } while ((e = (t = t->v.t + + ((unsigned)b & mask_bits[e]))->e) > 16); +#else + } while ((e = (t = (td + t->v.t) + + ((unsigned)b & mask_bits[e]))->e) > 16); +#endif + } +#ifdef DISPLAY + if (t->b) + { + printf("%d ",b & mask_bits[t->b]); + } +#endif + DUMPBITS(t->b) + + NEEDBITS(e) + if (Context->get_error) + { +#ifdef CK_DEBUG +printf("InflateCodes: bad 7\n"); +#endif + return(1); + } + + /* d is distance back in buffer */ + d = t->v.n + ((unsigned)b & mask_bits[e]); + +#ifdef DISPLAY + if (e) + { + printf("%d ",b & mask_bits[e]); + } +#endif + DUMPBITS(e) + +#ifdef DISPLAY_DECO + printf("%5ld %5ld: copy(%d,%d)\n",(unsigned long) Context->inptr, + (unsigned long) (Context->outbuffer - Context->outstart),d,n); +#endif + + /* do the copy */ + /* note: may reach back into "previous buffer" */ + /* previous buffer is loaded, so this may grab from end of buffer */ + +#ifdef NOWRAP + /* ifdef DRIVESPACE - don't worry about wrapping to previous buffer, */ + /* this is the easy case... */ + + dptr = Context->outbuffer - d; + +ResumeMatch: + + while (n--) + { + *Context->outbuffer++ = *dptr++; + if (--Context->outleft == 0) + { + IncrementalState = sHUFFTREE; + Context->streeIsMatch = TRUE; + Context->streeTL = tl; + Context->streeTD = td; + Context->streeBL = bl; + Context->streeBD = bd; + Context->streePointer = dptr; + Context->streeNumber = n; + goto Done; + } + } +#else + if (d > (unsigned)(Context->outbuffer - Context->outstart)) + { +/* ?? dptr = Context->outbuffer + (d & (WSIZE-1)); */ + dptr = Context->outbuffer + (WSIZE - d); + } + else + { + dptr = Context->outbuffer - d; + } + +ResumeMatch: + while (n--) + { + *Context->outbuffer++ = *dptr++; + + if (dptr == Context->outstart + WSIZE) + { + dptr = Context->outstart; + } + + if (--Context->outleft == 0) + { + Context->IncrementalState = sHUFFTREE; + Context->streeIsMatch = TRUE; + Context->streeTL = tl; + Context->streeTD = td; + Context->streeBL = bl; + Context->streeBD = bd; + Context->streePointer = dptr; + Context->streeNumber = n; + goto Done; + } + } +#endif + } + } + +Done: + + /* restore the globals from the locals */ + Context->bb = b; /* restore global bit buffer */ + Context->bk = k; + + /* done */ + return 0; +} + +/* --- InflateDynamic() --------------------------------------------------- */ + +static int NEAR InflateDynamic(PMSZIP_DECOMPRESS_CONTEXT Context) +/* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + HUFF_TREE *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ + register ULONG b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + int rc; /* return code */ + unsigned ll[286+30]; /* literal/length and distance code lengths */ + + b = Context->bb; /* setup local bit buffer */ + k = Context->bk; + + /* read in table lengths */ + + NEEDBITS(5) + nl = MASK5(b) + 257; /* number of literal/length codes */ + DUMPBITS(5) + + NEEDBITS(5) + nd = MASK5(b) + 1; /* number of distance codes */ + DUMPBITS(5) + + NEEDBITS(4) + nb = MASK4(b) + 4; /* number of bit length codes */ + DUMPBITS(4) + + if ((Context->get_error) || (nl > 286) || (nd > 30)) + { +#ifdef CK_DEBUG +printf("InflateDynamic: bad 1\n"); +#endif + return 1; /* bad lengths */ + } + + /* read in bit-length-code lengths */ + + for (j = 0; j < nb; j++) + { + NEEDBITS(3) + ll[border[j]] = MASK3(b); + DUMPBITS(3) + } + + while (j < 19) + { + ll[border[j++]] = 0; + } + + if (Context->get_error) + { +#ifdef CK_DEBUG +printf("InflateDynamic: bad 2\n"); +#endif + return(1); + } + + /* build decoding table for trees--single level, 7 bit lookup */ + + bl = 7; + + rc = HuftBuild(Context, ll, 19, 19, NULL, NULL, Context->htl, HTL_SIZE, &bl); + + if (rc != 0) + { +#ifdef CK_DEBUG +printf("InflateDynamic: bad 3\n"); +#endif + return rc; /* incomplete code set */ + } + + /* read in literal and distance code lengths */ + + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + + while ((unsigned)i < n) + { + NEEDBITS((unsigned)bl) + if (Context->get_error) + { + break; + } + + j = (td = Context->htl + ((unsigned)b & m))->b; + DUMPBITS(j) + + j = td->v.n; + + if (j < 16) /* length of code in bits (0..15) */ + { + ll[i++] = l = j; /* save last length in l */ + } + else if (j == 16) /* repeat last length 3 to 6 times */ + { + NEEDBITS(2) + if (Context->get_error) + { + break; + } + + j = MASK2(b) + 3; + DUMPBITS(2) + + if ((unsigned)i + j > n) + { + Context->get_error = 2; /* force it to free first */ + break; + } + + while (j--) + { + ll[i++] = l; + } + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + NEEDBITS(3) + if (Context->get_error) + { + break; + } + + j = MASK3(b) + 3; + DUMPBITS(3) + + if ((unsigned) i + j > n) + { + Context->get_error = 2; /* force it to free first */ + break; + } + + while (j--) + { + ll[i++] = 0; + } + + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + NEEDBITS(7) + if (Context->get_error) + { + break; + } + + j = MASK7(b) + 11; + DUMPBITS(7) + + if ((unsigned) i + j > n) + { + Context->get_error = 2; /* force it to free first */ + break; + } + + while (j--) + { + ll[i++] = 0; + } + + l = 0; + } + } + + if (Context->get_error) + { + return(1); + } + + /* restore the global bit buffer */ + Context->bb = b; + Context->bk = k; + + /* build the decoding tables for literal/length and distance codes */ + + bl = LBITS; + + rc = HuftBuild(Context, ll, nl, 257, cplens, cplext, Context->htl, HTL_SIZE, &bl); + if (rc != 0) + { + return rc; /* incomplete code set */ + } + + bd = DBITS; + + rc = HuftBuild(Context, ll + nl, nd, 0, cpdist, cpdext, Context->htd, HTD_SIZE, &bd); + if (rc != 0) + { + return rc; /* incomplete code set */ + } + + rc = InflateCodes(Context, Context->htl, Context->htd, bl, bd, FALSE); /* decompress using tables */ + + return rc; +} + +/* --- InitFixed() -------------------------------------------------------- */ + +/* Initialize the fixed huffman tables once only */ + +static int NEAR InitFixed(PMSZIP_DECOMPRESS_CONTEXT Context) +{ + int i; /* table fill looper */ + unsigned l[288]; /* length list for huft_build */ + int rc; /* result code */ + + i = 0; + + while (i < 144) + { + l[i++] = 8; + } + + while (i < 256) + { + l[i++] = 9; + } + + while (i < 280) + { + l[i++] = 7; + } + + while (i < 288) /* make a complete, but wrong code set */ + { + l[i++] = 8; + } + + rc = HuftBuild(Context, l, 288, 257, cplens, cplext, Context->fhtl, FIX_HTL_SIZE, &Context->fixedblit); + if (rc != 0) + { + return rc; /* ran out of memory */ + } + + for (i = 0; i < 30; i++) /* make an incomplete code set */ + { + l[i] = 5; + } + + rc = HuftBuild(Context, l, 30, 0, cpdist, cpdext, Context->fhtd, FIX_HTD_SIZE, &Context->fixedbdist); + + if (rc > 1) + { + return rc; + } + + return (0); +} + +/* --- InflateFixed() ----------------------------------------------------- */ + +/* decompress an inflated type 1 (fixed Huffman codes) block. */ + +static int NEAR InflateFixed(PMSZIP_DECOMPRESS_CONTEXT Context) +{ + /* inflate using fixed tables, and return result */ + + return InflateCodes(Context, Context->fhtl, Context->fhtd, Context->fixedblit, Context->fixedbdist, FALSE); +} + +/* --- InflateStored() ---------------------------------------------------- */ + +/* "decompress" a stored block */ +/* bResume is TRUE if we are resuming a previous operation */ + +static int NEAR InflateStored(PMSZIP_DECOMPRESS_CONTEXT Context,BOOL bResume) +{ + unsigned n; /* number of bytes in block */ + register ULONG b; /* local bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + BYTE *source; + + if (bResume) + { + source = Context->sstorePointer; + n = Context->sstoreNBytes; + + Context->IncrementalState = sNEWBLOCK; /* don't repeat */ + } + else + { + b = Context->bb; /* pickup current global buffer */ + k = Context->bk; + + n = (USHORT) (k & 7); /* advance to even byte boundary */ + DUMPBITS(n) + + /* get the stored block's length and maybe its complement */ + NEEDBITS(16) + n = (unsigned) (b & 0xffff); /* get length */ + DUMPBITS(16) + +#ifdef LARGE_STORED_BLOCKS + NEEDBITS(16) + + if (n != (~b & 0xFFFFL)) /* compare with complement */ + { +#ifdef CK_DEBUG +printf("Stored complement doesn't match\n"); +#endif + return(1); /* if complement doesn't jive */ + } + DUMPBITS(16) +#endif + + /* bit buffer should be empty now, since we just ate at least 3 bytes */ + + if (Context->get_error || k) /* if bit buffer isn't empty */ + { + return(1); /* this code assumes it is */ + } + + Context->bk = 0; /* "restore" (empty) global bit buffer */ + Context->bb = 0; + + source = &Context->inbuf[Context->inptr]; + + Context->inptr += n; /* advance source pointer too */ + } + + /* read and output the compressed data */ + /* circumvent the bit buffer for speed */ + +#ifdef DISPLAY_DECO + printf("%5ld %5ld: stored %u\n",(unsigned long) Context->inptr, + (unsigned long) (Context->outbuffer - Context->outstart),n); +#endif + + /* The entire stored block fits into the buffer, move it through */ + if (n <= Context->outleft) + { + Context->outleft -= n; + memcpy(Context->outbuffer, source, n); + Context->outbuffer += n; + } + /* The buffer will not hold the entire stored block, fill buffer and set checkpoint */ + else + { + memcpy(Context->outbuffer, source, Context->outleft); + Context->outbuffer += Context->outleft; + + Context->IncrementalState = sSTORE; + Context->sstoreNBytes = n - Context->outleft; + Context->sstorePointer = source + Context->outleft; + Context->outleft = 0; + } + + return 0; /* no error */ +} + +/* --- InflateBlock() ----------------------------------------------------- */ + +/* on exit: lastBlock set indicates last block was processed */ + +static int NEAR InflateBlock(PMSZIP_DECOMPRESS_CONTEXT Context) +{ + int blockType; /* block type */ + int rc; /* result code */ + ULONG b; /* local bit buffer */ + unsigned k; /* local number of bits in bit buffer */ + + b = Context->bb; /* get global buffer */ + k = Context->bk; + + NEEDBITS(1) + Context->lastBlock = MASK1(b); /* get last block bit 0,1 */ + DUMPBITS(1) + + NEEDBITS(2) + blockType = MASK2(b); /* get block type 0,1,2 */ + DUMPBITS(2) + + Context->bb = b; /* update global buffer */ + Context->bk = k; + + if (Context->get_error) + { + return(1); + } + + switch (blockType) + { + case STORED: /* stored block */ + rc = InflateStored(Context,FALSE); + break; + + case FIXED: /* fixed tree block */ + rc = InflateFixed(Context); + break; + + case DYNAMIC: /* dynamic tree block */ + rc = InflateDynamic(Context); + break; + + default: /* bad block type */ + rc = 2; + } + + return(rc); +} + +/* --- NFM_Prepare() ------------------------------------------------------ */ + +/* NFM_Prepare is used to initialize the decompressor for a new buffer. */ +/* All pointers are reset. */ + +int NFM_Prepare(void *context,BYTE FAR *bfSrc, UINT cbSrc, BYTE FAR *bfDest, UINT cbDest) +{ + PMSZIP_DECOMPRESS_CONTEXT Context = context; + + if (Context->fixed_init == 0) + { + if (InitFixed(Context) != 0) + { + return NFMoutofmem; + } + + Context->fixed_init = 1; + } + +#ifndef DRVSPACE + if ((bfSrc[0] != NFM_SIG0) || (bfSrc[1] != NFM_SIG1)) + { + return NFMinvalid; /* if block signature missing */ + } + + Context->inbuf = bfSrc + NFM_SIG_LEN; /* toss signature */ + Context->insize = cbSrc - NFM_SIG_LEN; +#else + Context->inbuf = bfSrc; /* no internal signature */ + Context->insize = cbSrc; +#endif + + Context->inptr = 0; + + /* initialize window, bit buffer */ + Context->bk = 0; + Context->bb = 0; + + Context->outstart = Context->outbuffer = bfDest; + Context->bufavail = cbDest; /* space available in buffer */ + + Context->lastBlock = 0; + Context->get_error = 0; /* reset error indicators */ + + Context->IncrementalState = sNEWBLOCK; /* always start at beginning of block */ + + return NFMsuccess; +} + + +/* --- NFM_Decompress() --------------------------------------------------- */ +/* + NFM_Decompress is used to decompress data. The buffers (input and output) + must have been set up with NFM_Prepare. +*/ +int NFM_Decompress(void *context,UINT FAR *pcbDestCount) +{ + PMSZIP_DECOMPRESS_CONTEXT Context = context; + int rc; /* return code */ + unsigned firstoutleft; /* space left in buffer when we begin */ + + Context->outleft = *pcbDestCount; + + /* avoid over-running the output buffer if too much data requested */ + if (Context->outleft > Context->bufavail) Context->outleft = Context->bufavail; + + firstoutleft = Context->outleft; + if (Context->outleft == 0) goto Done; + + switch (Context->IncrementalState) + { + case sNEWBLOCK: /* Starting fresh, no work left over */ + break; + + case sSTORE: + InflateStored(Context,TRUE); /* resume previous STORE operation */ + break; + + case sHUFFTREE: + InflateCodes(Context, Context->streeTL, Context->streeTD, Context->streeBL, Context->streeBD, TRUE); + break; + + case sDONE: /* No data left, return OK, 0 bytes */ + *pcbDestCount = 0; + return NFMsuccess; + + default: /* Impossible condition */ + return NFMinvalid; + } + + /* decompress until the last block or we finish request */ + while ((!Context->lastBlock) && (Context->outleft > 0)) + { + rc = InflateBlock(Context); + + if (rc != 0) + { + if (rc == 3) + { + return NFMoutofmem; /* if ran out internally */ + } + else + { + return NFMinvalid; /* if bad input data */ + } + } + } + +Done: + *pcbDestCount = (USHORT) (firstoutleft - Context->outleft); /* set output count */ + + return NFMsuccess; /* return to caller */ +} + +void * +NFMInitializeContext( + PFNALLOC NFMalloc + ) +{ + PMSZIP_DECOMPRESS_CONTEXT p; + + if(p = NFMalloc(sizeof(MSZIP_DECOMPRESS_CONTEXT))) { + + memset(p,0,sizeof(MSZIP_DECOMPRESS_CONTEXT)); + + p->fixedblit = 7; /* number of bits in literal tree */ + p->fixedbdist = 5; /* number of bits in distance tree */ + } + return(p); +} + +void +NFMDestroyContext( + void *Context, + PFNFREE NFMfree + ) +{ + NFMfree(Context); +} + +/* --- DisplayTree() ------------------------------------------------------ */ + +#ifdef DISPLAY + +static void NEAR DisplayTree(HUFF_TREE *tree) +{ + static int depth = -1; + int tab; + int element; + BYTE e,b; + + depth++; /* recursion depth controls indentation */ + + for (element = 0; element < NNodes; element++) + { + for (tab = 0; tab < depth; tab++) + { + printf(" "); + } + printf("%3d: ",element); + + e = tree[element].e; + b = tree[element].b; + + if (e < 14) + { + printf("extra bits=%u, b=%u, n=%u\n",e,b,tree[element].v.n); + } + else if (e == 15) + { + printf("<end of block>\n"); + } + else if (e == 16) + { + printf("literal %02X",tree[element].v.n); + if ((tree[element].v.n >= ' ') && (tree[element].v.n <= '~')) + { + printf(" '%c'",tree[element].v.n); + } + printf("\n"); + } + else if ((e > 16) && (e < 32)) + { + printf("sub table codes %d bit(s), b=%d\n",(e-16),b); +#ifdef POINTERS + DisplayTree(tree[element].v.t); /* recurse */ +#else + DisplayTree(tree + (tree[element].v.t)); /* recurse */ +#endif + } + else if (e == 99) + { + printf("<unused code>\n"); + } + else + { + printf("<illegal code e=%d>\n",e); + } + } + + depth--; /* handle display formatting */ +} + +#endif + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/mszip/nfmdeco.h b/private/windows/diamond/mszip/nfmdeco.h new file mode 100644 index 000000000..8abd9917d --- /dev/null +++ b/private/windows/diamond/mszip/nfmdeco.h @@ -0,0 +1,102 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993,1994,1995 + * All Rights Reserved. + * + * NFMDECO.H -- features of NFMDECO.C, the NF decompressor + * + * History: + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * normalized MCI_MEMORY type. + * 24-Feb-1994 msliger Changed MDI_MEMORY to MI_MEMORY. + * 22-Mar-1994 msliger Changed !INT32 to BIT16. + * Changed interface USHORT -> UINT. + * 06-Apr-1994 msliger Defined UNALIGNED for RISC. + * 13-Apr-1994 msliger Defined call convention for alloc/free. + * 12-May-1994 msliger ifdef'd 1's complement LARGE_STORED_BLOCKS. + * 15-Nov-1994 msliger No longer needs alloc/free. + * 25-May-1995 msliger Dropped NFMuncompress, added NFM_Prepare() + * and NFM_Decompress(). + * 12-Jun-1995 msliger Found cases to increase MAX_GROWTH. + */ + +/* --- types -------------------------------------------------------------- */ + +#ifndef DIAMONDAPI +#define DIAMONDAPI __cdecl +#endif + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif + +#ifndef _UINT_DEFINED +#define _UINT_DEFINED +typedef unsigned int UINT; +#endif + +#ifndef _ULONG_DEFINED +#define _ULONG_DEFINED +typedef unsigned long ULONG; +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE huge +#else +#define HUGE +#endif +#endif + +#ifndef UNALIGNED +#ifdef NEEDS_ALIGNMENT +#define UNALIGNED __unaligned +#else /* !NEEDS_ALIGNMENT */ +#define UNALIGNED +#endif /* NEEDS_ALIGNMENT */ +#endif /* UNALIGNED */ + +/* --- prototypes --------------------------------------------------------- */ + +extern int NFM_Prepare(void *context, BYTE FAR *bfSrc, UINT cbSrc, + BYTE FAR *bfDest, UINT cbDest); + +extern int NFM_Decompress(void *context, UINT FAR *pcbDestCount); + +void * +NFMInitializeContext( + PFNALLOC NFMalloc + ); + +void +NFMDestroyContext( + void *Context, + PFNFREE NFMfree + ); + +/* --- constants ---------------------------------------------------------- */ + +/* return codes */ + +#define NFMsuccess 0 /* successful completion */ +#define NFMdestsmall 1 /* destination buffer is too small */ +#define NFMoutofmem 2 /* alloc returned an error (NULL) */ +#define NFMinvalid 3 /* source buffer contains bad data */ + + +#ifdef LARGE_STORED_BLOCKS +#define MAX_GROWTH 12 /* maximum growth of a block */ +#else +#define MAX_GROWTH 8 /* maximum growth of a block */ +#endif + +/* ----------------------------------------------------------------------- */ diff --git a/private/windows/diamond/qstub/makefile b/private/windows/diamond/qstub/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/qstub/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/qstub/qstub.c b/private/windows/diamond/qstub/qstub.c new file mode 100644 index 000000000..cb07e3575 --- /dev/null +++ b/private/windows/diamond/qstub/qstub.c @@ -0,0 +1,142 @@ +#include <windows.h> + +#include "qci.h" +#include "qdi.h" + + + +int +DIAMONDAPI +QCICreateCompression( + UINT * pcbDataBlockMax, + void * pvConfiguration, + PFNALLOC pfnma, + PFNFREE pfnmf, + UINT * pcbDstBufferMin, + QCI_CONTEXT_HANDLE *pmchHandle + ) +{ + UNREFERENCED_PARAMETER(pcbDataBlockMax); + UNREFERENCED_PARAMETER(pvConfiguration); + UNREFERENCED_PARAMETER(pfnma); + UNREFERENCED_PARAMETER(pfnmf); + UNREFERENCED_PARAMETER(pcbDstBufferMin); + UNREFERENCED_PARAMETER(pmchHandle); + return(MCI_ERROR_FAILED); +} + + +int +DIAMONDAPI +QCIResetCompression( + QCI_CONTEXT_HANDLE hmc + ) +{ + UNREFERENCED_PARAMETER(hmc); + return(MCI_ERROR_FAILED); +} + + +int +DIAMONDAPI +QCIDestroyCompression( + QCI_CONTEXT_HANDLE hmc + ) +{ + UNREFERENCED_PARAMETER(hmc); + return(MCI_ERROR_FAILED); +} + + +int +DIAMONDAPI +QCICompress( + QCI_CONTEXT_HANDLE hmc, + void * pbSrc, + UINT cbSrc, + void * pbDst, + UINT cbDst, + UINT * pcbResult + ) +{ + UNREFERENCED_PARAMETER(hmc); + UNREFERENCED_PARAMETER(pbSrc); + UNREFERENCED_PARAMETER(cbSrc); + UNREFERENCED_PARAMETER(pbDst); + UNREFERENCED_PARAMETER(cbDst); + UNREFERENCED_PARAMETER(pcbResult); + return(MCI_ERROR_FAILED); +} + + + +int +DIAMONDAPI +QDICreateDecompression( + UINT *pcbDataBlockMax, + void *pvConfiguration, + PFNALLOC pfnma, + PFNFREE pfnmf, + UINT *pcbSrcBufferMin, + QDI_CONTEXT_HANDLE *pmdhHandle, + PFNOPEN pfnopen, + PFNREAD pfnread, + PFNWRITE pfnwrite, + PFNCLOSE pfnclose, + PFNSEEK pfnseek + ) +{ + UNREFERENCED_PARAMETER(pcbDataBlockMax); + UNREFERENCED_PARAMETER(pvConfiguration); + UNREFERENCED_PARAMETER(pfnma); + UNREFERENCED_PARAMETER(pfnmf); + UNREFERENCED_PARAMETER(pcbSrcBufferMin); + UNREFERENCED_PARAMETER(pmdhHandle); + UNREFERENCED_PARAMETER(pfnopen); + UNREFERENCED_PARAMETER(pfnread); + UNREFERENCED_PARAMETER(pfnwrite); + UNREFERENCED_PARAMETER(pfnclose); + UNREFERENCED_PARAMETER(pfnseek); + return(MDI_ERROR_FAILED); +} + + +int +DIAMONDAPI +QDIResetDecompression( + QDI_CONTEXT_HANDLE hmd + ) +{ + UNREFERENCED_PARAMETER(hmd); + return(MDI_ERROR_FAILED); +} + + +int +DIAMONDAPI +QDIDestroyDecompression( + QDI_CONTEXT_HANDLE hmd + ) +{ + UNREFERENCED_PARAMETER(hmd); + return(MDI_ERROR_FAILED); +} + + +int +DIAMONDAPI +QDIDecompress( + QDI_CONTEXT_HANDLE hmd, + void *pbSrc, + UINT cbSrc, + void *pbDst, + UINT *pcbResult + ) +{ + UNREFERENCED_PARAMETER(hmd); + UNREFERENCED_PARAMETER(pbSrc); + UNREFERENCED_PARAMETER(cbSrc); + UNREFERENCED_PARAMETER(pbDst); + UNREFERENCED_PARAMETER(pcbResult); + return(MDI_ERROR_FAILED); +} diff --git a/private/windows/diamond/qstub/sources b/private/windows/diamond/qstub/sources new file mode 100644 index 000000000..e06e92414 --- /dev/null +++ b/private/windows/diamond/qstub/sources @@ -0,0 +1,10 @@ +MAJORCOMP=diamond +MINORCOMP=quantum_stub + +TARGETNAME=qstub +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +INCLUDES=..\quantum + +SOURCES=qstub.c diff --git a/private/windows/diamond/quantum/arc.h b/private/windows/diamond/quantum/arc.h new file mode 100644 index 000000000..3e196da50 --- /dev/null +++ b/private/windows/diamond/quantum/arc.h @@ -0,0 +1,28 @@ +// ARC.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __ARC +#define __ARC + +#include <stdio.h> +#include "quantum.h" + + +typedef struct + { + FILE *ArcFile, *TxtFile; + APARMS *AParms; + int OpenCounter; + } ARC; + + +void Arc_Header_Read( APARMS *AParms ); +void Arc_Create( APARMS *AParms ); +void Arc_Extract( APARMS *AParms, BOOL ExtractFlag ); + +#endif // arc.h diff --git a/private/windows/diamond/quantum/arith.c b/private/windows/diamond/quantum/arith.c new file mode 100644 index 000000000..bc30a0e89 --- /dev/null +++ b/private/windows/diamond/quantum/arith.c @@ -0,0 +1,364 @@ +// ARITH.C +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 Cinematronics +// All rights reserved. +// +// This file contains trade secrets of Cinematronics. +// Do NOT distribute! + +#include "arith.h" +#include "bit.h" + + +#define CODE_VALUE_BITS 16 // number of bits in a code value + +#define TOP_VALUE ((1L << CODE_VALUE_BITS)-1) // largest code value + + +#ifdef PAQ + +struct + { + unsigned short Low, High; + unsigned short Code; + int UnderflowBits; + } Arith; + + +void FAST Arith_Init( void ) + { + Bit_Init(); + + Arith.Low = 0; + Arith.High = TOP_VALUE; + Arith.UnderflowBits = 0; + } + + +// Flush the remaining significant bits. + +void FAST Arith_Close( void ) + { + Bit_Write( Arith.Low & 0x4000 ); + + Arith.UnderflowBits++; + + while( Arith.UnderflowBits > 0 ) + { + Bit_Write( ~Arith.Low & 0x4000 ); + + Arith.UnderflowBits--; + } + + // Just output enough bits to fill another word so we don't + // run out of bits in the final call to Arith_Remove_Symbol. + + Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); + Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); + Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); + Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); Bit_Write( 0 ); + + Bit_Flush(); + } + + +void FAST Arith_Encode_Bits( int Value, int NumBits ) + { +#ifdef BIT_CONSTANTS + Bit_Delayed_Write( CODE_VALUE_BITS + Arith.UnderflowBits, Value, NumBits ); +#else + SYMBOL Symbol; + int V; + + if( NumBits >= 12 ) + { + NumBits -= 12; + + V = Value >> NumBits; + + Symbol.Scale = 1 << 12; + Symbol.Low = V & ((1 << 12) - 1); + Symbol.High = Symbol.Low + 1; + + Arith_Encode_Symbol( &Symbol ); + } + + if( NumBits ) + { + Symbol.Scale = 1 << NumBits; + Symbol.Low = Value & (Symbol.Scale - 1); + Symbol.High = Symbol.Low + 1; + + Arith_Encode_Symbol( &Symbol ); + } +#endif + } + + +void FAST Arith_Encode_Symbol( SYMBOL *Symbol ) + { + unsigned long Range; + + // These three lines rescale high and low for the new symbol. + + Range = (unsigned long) (Arith.High - Arith.Low) + 1L; + Arith.High = Arith.Low + + (unsigned short)((Range * Symbol->High) / Symbol->Scale - 1); + Arith.Low = Arith.Low + + (unsigned short)((Range * Symbol->Low) / Symbol->Scale); + + // This loop turns out new bits until high and low are far enough + // apart to have stabilized. + + while( 1 ) + { + // If this test passes, it means that the MSDigits match, and can + // be sent to the output stream. + + if( (Arith.High & 0x8000) == (Arith.Low & 0x8000) ) + { + Bit_Write( Arith.High & 0x8000 ); + + while( Arith.UnderflowBits > 0 ) + { + Bit_Write( ~Arith.High & 0x8000 ); + + Arith.UnderflowBits--; + } + } + else + { + // If this test passes, the numbers are in danger of underflow, because + // the MSDigits don't match, and the 2nd digits are just one apart. + + if( (Arith.Low & 0x4000) && !(Arith.High & 0x4000) ) + { + Arith.UnderflowBits++; + + Arith.Low &= 0x3FFF; + Arith.High |= 0x4000; + } + else + { + return; + } + } + + Arith.Low <<= 1; + Arith.High <<= 1; + + Arith.High++; + } + } + +#else // UNPAQ + +#ifdef UNPAQLIB + +extern char *pbSource; +extern int cbSource; +extern int fSourceOverflow; + +static int BitsValid; +static int BitsValue; + +#define Bit_Init() /* void Bit_Init(void) */ \ + ( /* { */ \ + BitsValid = 0 /* BitsValid = 0; */ \ + ) /* } */ + +#define Bit_Read() /* int Bit_Read(void) */ \ + ( /* { */ \ + BitsValid ? /* if (BitsValid != 0) */ \ + ( /* { */ \ + BitsValid--, /* BitsValid--; */ \ + BitsValue <<= 1, /* BitsValue <<= 1; */ \ + (BitsValue & 0x0100) /* return(BitsValue & 0x0100); */ \ + ) /* } */ \ + : /* else */ \ + ( /* { */ \ + cbSource ? /* if (cbSource != 0) */ \ + ( /* { */ \ + cbSource--, /* cbSource--; */ \ + BitsValid = 7, /* BitsValid = 7; */ \ + BitsValue = *pbSource++, /* BitsValue = *pbSource++; */ \ + BitsValue <<= 1, /* BitsValue <<= 1; */ \ + (BitsValue & 0x0100) /* return(BitsValue & 0x0100); */ \ + ) /* } */ \ + : /* else */ \ + ( /* { */ \ + fSourceOverflow = 1, /* fSourceOverflow = 1; */ \ + 0 /* return(0); */ \ + ) /* } */ \ + ) /* } */ \ + ) /* } */ + +#endif /* def UNPAQLIB */ + +struct + { + unsigned short Low, High; + unsigned short Code; + } Arith; + + +void FAST Arith_Init( void ) + { + int i; + + Bit_Init(); + + for( i = sizeof( Arith.Code ) * 8; i; i-- ) + { + Arith.Code <<= 1; + if (Bit_Read()) + { + Arith.Code |= 1; + } + } + + Arith.Low = 0; + Arith.High = TOP_VALUE; + } + + +long FAST Arith_Decode_Bits( int NumBits ) + { +#ifdef BIT_CONSTANTS + register long Value = 0; + + while( NumBits-- ) + { + Value <<= 1; + + if (Bit_Read()) + { + Value |= 1; + } + } + + return( Value ); +#else + long Value = 0; + SYMBOL Symbol; + int i; + + if( NumBits >= 12 ) + { + NumBits -= 12; + + Symbol.Scale = 1 << 12; + + Value = Arith_GetCount( Symbol.Scale ); + + Symbol.Low = Value; + Symbol.High = Value + 1; + + Arith_Remove_Symbol( Symbol ); + } + + if( NumBits ) + { + Symbol.Scale = 1 << NumBits; + + i = Arith_GetCount( Symbol.Scale ); + + Symbol.Low = i; + Symbol.High = i + 1; + + Arith_Remove_Symbol( Symbol ); + + Value = (Value << NumBits) + i; + } + + return( Value ); +#endif + } + + +/* + * When decoding, this routine is called to figure out which symbol + * is presently waiting to be decoded. This routine expects to get + * the current model scale in the s->scale parameter, and it returns + * a count that corresponds to the present floating point code: + * + * code = count / s->scale + */ + +int FAST2 Arith_GetCount( unsigned short Scale ) + { + unsigned long Range; + short int Count; + + Range = (unsigned long) (Arith.High - Arith.Low) + 1L; + Count = (short) ( (( + (unsigned long)(Arith.Code - Arith.Low) + 1L) * Scale - 1) / Range); + + return( Count ); + } + + +void FAST Arith_Close( void ) + { + } + + +/* + * Just figuring out what the present symbol is doesn't remove + * it from the input bit stream. After the character has been + * decoded, this routine has to be called to remove it from the + * input stream. + */ + +void FAST2 Arith_Remove_Symbol( SYMBOL Symbol ) + { + unsigned long Range; + + // First, the range is expanded to account for the symbol removal. + + Range = (unsigned long) (Arith.High - Arith.Low) + 1L; + Arith.High = Arith.Low + + (unsigned short)((Range * Symbol.High) / Symbol.Scale - 1); + Arith.Low = Arith.Low + + (unsigned short)((Range * Symbol.Low) / Symbol.Scale); + + // Next, any possible bits are shipped out. + + while( 1 ) + { + // If the MSDigits match, the bits will be shifted out. + + if ((Arith.High ^ Arith.Low) & 0x8000) + { + // Else, if underflow is threatening, shift out the 2nd MSDigit. + + if( (Arith.Low & 0x4000) && (Arith.High & 0x4000) == 0 ) + { + Arith.Code ^= 0x4000; + Arith.Low &= 0x3FFF; + Arith.High |= 0x4000; + } + else + { + // Otherwise, nothing can be shifted out, so I return. + + return; + } + } + + Arith.Low <<= 1; + Arith.High <<= 1; + Arith.High |= 1; + Arith.Code <<= 1; + + if (Bit_Read()) + { + Arith.Code |= 1; + } + } + } + +#endif /* PAQ */ diff --git a/private/windows/diamond/quantum/arith.h b/private/windows/diamond/quantum/arith.h new file mode 100644 index 000000000..c80bf349a --- /dev/null +++ b/private/windows/diamond/quantum/arith.h @@ -0,0 +1,32 @@ +// ARITH.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __ARITH +#define __ARITH + +#include "defs.h" + +typedef struct + { + unsigned short Low, High; + unsigned short Scale; + } SYMBOL; + + +void FAST Arith_Init( void ); +void FAST Arith_Close( void ); + +void FAST Arith_Encode_Symbol( SYMBOL *Symbol ); +void FAST Arith_Encode_Bits( int Value, int NumBits ); + +long FAST Arith_Decode_Bits( int NumBits ); + +int FAST2 Arith_GetCount( unsigned short Scale ); +void FAST2 Arith_Remove_Symbol( SYMBOL Symbol ); + +#endif // arith.h diff --git a/private/windows/diamond/quantum/bit.c b/private/windows/diamond/quantum/bit.c new file mode 100644 index 000000000..ebf9b8d65 --- /dev/null +++ b/private/windows/diamond/quantum/bit.c @@ -0,0 +1,201 @@ +// BIT.C +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 Cinematronics +// All rights reserved. +// +// This file contains trade secrets of Cinematronics. +// Do NOT distribute! + +#include <stdio.h> +#include <stdlib.h> + +#ifndef PAQLIB //msliger +#include "arc.h" +#endif //msliger + +#ifdef PAQLIB //msliger +#include "quantum.h" //msliger +#endif //msliger + +#include "bit.h" + +#ifndef PAQLIB //msliger +extern ARC Arc; //msliger +#endif //msliger + +#ifdef PAQLIB //msliger +extern char *pbTarget; //msliger +extern int cbTarget; //msliger +extern int fTargetOverflow; //msliger +extern int cbResult; //msliger +#endif //msliger + + +#define DELAYED_WRITE_QUEUE_SIZE 512 + +typedef struct _DELAYED_WRITE + { + long BitCounter; // when TotalBits == BitCounter, write it out + int Value; + int NumBits; + struct _DELAYED_WRITE *Next; // for the circular queue + } DELAYED_WRITE; + + +struct + { + int NumBits; + long TotalBits; + short This; + +#ifdef BIT_CONSTANTS + DELAYED_WRITE *Head, *Tail; // circular queue of delayed writes + DELAYED_WRITE Queue[ DELAYED_WRITE_QUEUE_SIZE ]; + long QueueSize; // number of bits waiting in the queue +#endif + } Bit; + + +#ifdef PAQ //msliger +#ifndef PAQLIB //msliger +long Bit_GetTotal( void ) + { + return( Bit.TotalBits ); + } +#endif //PAQLIB //msliger +#endif //PAQ //msliger + + +void Bit_Init( void ) + { +#ifdef BIT_CONSTANTS //msliger + int i; +#endif //msliger + + Bit.TotalBits = Bit.NumBits = 0; + +#ifdef BIT_CONSTANTS + for( i = 0; i < DELAYED_WRITE_QUEUE_SIZE - 1; i++ ) + { + Bit.Queue[ i ].Next = &Bit.Queue[ i + 1 ]; + } + + Bit.Queue[ DELAYED_WRITE_QUEUE_SIZE - 1 ].Next = Bit.Queue; + + Bit.Head = Bit.Tail = Bit.Queue; + Bit.QueueSize = 0; +#endif + } + +#ifdef UNPAQ //msliger + +int Bit_Read( void ) + { + if( !Bit.NumBits ) + { + Bit.NumBits = 8; + + if( (Bit.This = fgetc( Arc.ArcFile )) == EOF ) + { + puts( "ERR: Premature EOF in input." ); + exit( -1 ); + } + } + + Bit.This <<= 1; + Bit.NumBits--; + + return( Bit.This & 0x0100 ); + } + +#endif /* UNPAQ */ //msliger + +#ifdef PAQ //msliger + +void Bit_Write( int Value ) + { + Bit.This <<= 1; + + if( Value ) Bit.This++; + + Bit.TotalBits++; + + if( ++Bit.NumBits == 8 ) + { + Bit.NumBits = 0; +#ifndef PAQLIB //msliger + putc( Bit.This, Arc.ArcFile ); +#else //msliger + if (cbTarget) //msliger + { //msliger + cbTarget--; //msliger + *pbTarget++ = (BYTE) Bit.This; //msliger + cbResult++; //msliger + } //msliger + else //msliger + { //msliger + fTargetOverflow = 1; //msliger + } //msliger +#endif //msliger + } + +#ifdef BIT_CONSTANTS + // Write out any bits that have been buffered. + + while( Bit.QueueSize && Bit.Head->BitCounter == Bit.TotalBits ) + { + Bit.QueueSize -= Bit.Head->NumBits; + + while( Bit.Head->NumBits-- ) + { + Bit_Write( Bit.Head->Value & (1L << Bit.Head->NumBits) ); + } + + Bit.Head = Bit.Head->Next; + } +#endif + } + + +#ifdef BIT_CONSTANTS +void Bit_Delayed_Write( int Delay, int Value, int NumBits ) + { + if( NumBits ) + { + Bit.Tail->BitCounter = Bit.TotalBits + Bit.QueueSize + Delay; + Bit.Tail->Value = Value; + Bit.Tail->NumBits = NumBits; + + Bit.Tail = Bit.Tail->Next; + + Bit.QueueSize += NumBits; + + if( Bit.Tail == Bit.Head ) + { + puts( "ERR: Delayed-write buffer overflowed." ); + exit( -1 ); + } + } + } +#endif + + +void Bit_Flush( void ) + { + while( Bit.NumBits ) + { + Bit_Write( 0 ); + } + +#ifdef BIT_CONSTANTS + if( Bit.QueueSize ) + { + puts( "ERR: Delayed-write buffer underflowed." ); + exit( -1 ); + } +#endif + } +#endif /* PAQ */ //msliger diff --git a/private/windows/diamond/quantum/bit.h b/private/windows/diamond/quantum/bit.h new file mode 100644 index 000000000..fe3fc6ddc --- /dev/null +++ b/private/windows/diamond/quantum/bit.h @@ -0,0 +1,20 @@ +// BIT.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __BITIO +#define __BITIO + + +void Bit_Init( void ); +int Bit_Read( void ); +void Bit_Write( int Value ); +void Bit_Delayed_Write( int Delay, int Value, int NumBits ); +void Bit_Flush( void ); +long Bit_GetTotal( void ); + +#endif // bit.h diff --git a/private/windows/diamond/quantum/comp.c b/private/windows/diamond/quantum/comp.c new file mode 100644 index 000000000..1d066fdee --- /dev/null +++ b/private/windows/diamond/quantum/comp.c @@ -0,0 +1,1103 @@ +// COMP.C +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 Cinematronics +// All rights reserved. +// +// This file contains trade secrets of Cinematronics. +// Do NOT distribute! + +/* + * History + * + * 06-Jul-1994 msliger fixes for MIPS + */ + +#include <stdio.h> +#include <stdlib.h> + +#ifdef STAFFORD +#include <alloc.h> +#include <mem.h> +#else +#include <string.h> // Get memmove() +#endif // STAFFORD + +#include "quantum.h" +#ifndef PAQLIB //msliger +#include "arc.h" +#endif //msliger +#include "rtl.h" +#include "comp.h" +#include "lz.h" + +#ifdef PAQLIB //msliger +#include "arith.h" //msliger +#endif //msliger + + +#ifndef PAQLIB //msliger +extern ARC Arc; +#endif //msliger + + +#define HASH_SHIFT 6 +#define HASH_BITS (HASH_SHIFT * 3) + +#define HASH_MASK ((1L << HASH_BITS) - 1) +#define HASH_UPDATE( x ) \ + (Comp.HashVal = (WORD)(((Comp.HashVal << HASH_SHIFT) ^ (x)) & HASH_MASK)) + //4 1 1321 1 1 12 34 + + +#define HASH_TABLE_SIZE (1L << HASH_BITS) + +#define MAX_MOVES 8 +#define MAX_MATCHES (MAX_MOVES-1) + + +typedef struct + { +#ifndef PAQLIB //msliger + FILE *TxtFile; // the current file being compressed +#else //msliger + BYTE *pbSource; //msliger the source data buffer + int cbSource; //msliger available source data size + BYTE WindowBits; //msliger needed in Comp_Reset() +#endif //msliger + + BYTE *Buf; // the file buffer + int Cur; // current position in the buffer + int Left; // left and right edges in the buffer + int Right; + + long BufSize; // size of the buffer + + DWORD FileSize; // size of the file being compressed + DWORD Compressed; // bytes compressed so far + + int *MatchTable; // hash table for identifying matches + int *MatchChain; // match chain + + long WindowSize; // size of the history window + + long ValidDistances[ MATCH_MAX + 1 ]; // valid distances for each match len + + WORD HashVal; // current hash value + + int MatchFutilityCutoff; + int MaxMatches; + +#ifndef PAQLIB //msliger + BOOL ShowStatus; + + WORD Checksum; +#endif //msliger + //msliger + int ReachedEOF; //msliger track EOF, don't refill after + //msliger + int CompressionLevel; //msliger selected compression level + } COMP; + + +COMP Comp; + + +// Fills the buffer for compression. + +void Comp_FillBuf( void ) + { + static int c; + int i; + + // Need to move the characters down the buffer? + + if( Comp.Left > 0 ) + { + memmove( Comp.Buf, Comp.Buf + Comp.Left, Comp.Right - Comp.Left ); + + memmove( Comp.MatchChain, + Comp.MatchChain + Comp.Left, + (Comp.Right - Comp.Left) * sizeof( *Comp.MatchChain ) ); + + for( i = 0; i < HASH_TABLE_SIZE; i++ ) + { + Comp.MatchTable[ i ] -= Comp.Left; + + if( Comp.MatchTable[ i ] < 0 ) Comp.MatchTable[ i ] = -1; + } + + Comp.Cur -= Comp.Left; + Comp.Right -= Comp.Left; + + for( i = 0; i < Comp.Cur; i++ ) + { + if (Comp.MatchChain[ i ] >= 0) //msliger + { //msliger + Comp.MatchChain[ i ] -= Comp.Left; + //msliger + if( Comp.MatchChain[ i ] < 0 ) Comp.MatchChain[ i ] = -1; //msliger + } //msliger + } + + Comp.Left = 0; + } + + // Fill the rest of the buffer from the input file. + + c = ~EOF; //msliger + + while( Comp.Right < Comp.BufSize && +#ifndef PAQLIB //msliger + (c = getc( Comp.TxtFile )) != EOF ) +#else //msliger + (c = Comp.cbSource ? //msliger + (Comp.cbSource--, *Comp.pbSource++) : EOF) != EOF) //msliger +#endif //msliger + { + Comp.Buf[ Comp.Right++ ] = c; + +#ifndef PAQLIB //msliger + Comp.Checksum ^= c; + + if( Comp.Checksum & 0x8000 ) // simulate a ROL in C, yucky! + { + Comp.Checksum <<= 1; + Comp.Checksum++; + } + else + { + Comp.Checksum <<= 1; + } +#endif // !PAQLIB //msliger + } + if (c == EOF) //msliger + { //msliger + Comp.ReachedEOF = 1; //msliger + } //msliger + } + + +#include <sys\stat.h> + +void Comp_Init( BYTE WindowBits, int CompressionLevel ) //msliger + { + int i; + long MinBufSize; + BYTE *TotalBuf; + void *Reserved; +#ifndef PAQLIB //msliger + struct stat statbuf; + + // If stdout is redirected to a file don't + // display the percent-complete info. + + fstat( fileno( stdout ), &statbuf ); + Comp.ShowStatus = (statbuf.st_mode & S_IFCHR); +#endif //msliger + + Comp.WindowSize = 1 << WindowBits; + Comp.CompressionLevel = CompressionLevel; //msliger + +#ifdef PAQLIB //msliger + Comp.WindowBits = WindowBits; //msliger +#endif //msliger + + Comp.ValidDistances[ MATCH_MIN ] = (1L << TINY_WINDOW_MAX); + Comp.ValidDistances[ MATCH_MIN + 1 ] = (1L << SHORT_WINDOW_MAX); + + for( i = MATCH_MIN + 2; i <= MATCH_MAX; i++ ) + { + Comp.ValidDistances[ i ] = (1L << WINDOW_MAX) - 1; + } + + Lz_Init( WindowBits ); //msliger + + // Set aside 64K of reserved space so we don't use all + // available memory for the history buffer. This is just + // to make sure there is room for file buffers, growing + // the stack etc later. + + Reserved = Rtl_Malloc( 64 * 1024L ); + + Comp.MatchTable = Rtl_Malloc( HASH_TABLE_SIZE * sizeof( int ) ); + + MinBufSize = Comp.WindowSize + MATCH_MAX + 1024; + +#if 1 + for( Comp.BufSize = MinBufSize * 2; + Comp.BufSize >= MinBufSize; + Comp.BufSize -= 0x8000 ) + { + TotalBuf = Rtl_Malloc( Comp.BufSize * (sizeof( int ) + 1) ); + + if( TotalBuf ) break; + } +#else + Comp.BufSize = MinBufSize * 5 / 4; + + TotalBuf = Rtl_Malloc( Comp.BufSize * (sizeof( int ) + 1) ); +#endif + + if( !TotalBuf || !Reserved || !Comp.MatchTable ) + { + puts( "ERR: Out of memory." ); + exit( -1 ); + } + + Rtl_Free( Reserved ); + + /* moved the int's first to dodge a MIPS alignment fault */ + + Comp.MatchChain = (int *) TotalBuf; + Comp.Buf = TotalBuf + Comp.BufSize * sizeof(int); + + if( Comp.BufSize < MinBufSize * 4 / 3 ) + { + if( Comp.BufSize < MinBufSize * 8 / 7 ) + { + puts( "\nCritically short of memory." ); + puts( "Compression will be much slower." ); + } + else + { + puts( "\nA few quarts low on memory." ); + puts( "Compression will be slower." ); + } + } + +//BUGBUG 25-May-94 msliger Init of hash tables should be done with memset + for( i = 0; i < HASH_TABLE_SIZE; i++ ) + { + Comp.MatchTable[ i ] = -1; + } + + for( i = 0; i < Comp.BufSize; i++ ) + { + Comp.MatchChain[ i ] = -1; + } + + Comp.Left = Comp.Cur = Comp.Right = 0; + } + + +// Searches for the longest match in the buffer. +// Returns the match length. + +int Comp_FindLongestMatch( MATCH *Match ) + { + BYTE *Str; + BYTE *Cur; + int Rover; + int More = Comp.MatchFutilityCutoff; + int Len; + int Max; + + Match->Len = 0; + + if( (Rover = Comp.MatchTable[ Comp.HashVal ]) < 0 ) + { + return( 0 ); + } + + Max = min( Comp.Right - Comp.Cur, MATCH_MAX ); + + if( Max >= MATCH_MIN ) + { + while( Rover >= Comp.Left ) + { + Cur = Comp.Buf + Comp.Cur; + Str = Comp.Buf + Rover; + Len = 0; + + while( *Str == *Cur && Len < Max ) + { + Len++; + Str++; + Cur++; + } + + if( Len > Match->Len && Len >= MATCH_MIN ) + { + if( Cur - Str <= Comp.ValidDistances[ Len ] ) + { + More = Comp.MatchFutilityCutoff; + + Match->Len = Len; + Match->Dist = Cur - Str; + + if( Len == Max ) return( Len ); + } + } + + if( !--More ) return( Match->Len ); + + Rover = Comp.MatchChain[ Rover ]; + } + } + + return( Match->Len ); + } + + +// Searches for the longest matches in the buffer. +// Returns the number of matches found. +// +// The matches are returned in order of length, that is, +// Match[ 0 ] is the longest. + +int New_Comp_FindLongestMatches( MATCH Match[ MAX_MATCHES ] ) + { + BYTE *Str, *Cur; + int i, NumMatches = 0, Rover, More = Comp.MatchFutilityCutoff; + int Len; + int Max; + + Match[ 0 ].Len = 0; + + if( (Rover = Comp.MatchTable[ Comp.HashVal ]) < 0 ) + { + return( 0 ); + } + + Max = min( Comp.Right - Comp.Cur, MATCH_MAX ); + + if( Max >= MATCH_MIN ) + { + while( Rover >= Comp.Left ) + { + Cur = Comp.Buf + Comp.Cur; + Str = Comp.Buf + Rover; + Len = 0; + + while( *Str == *Cur && Len < Max ) + { + Len++; + Str++; + Cur++; + } + + if( Len > Match[ 0 ].Len && Len >= MATCH_MIN ) + { + if( Cur - Str <= Comp.ValidDistances[ Len ] ) + { + More = Comp.MatchFutilityCutoff; + + // Move the other matches down + for( i = Comp.MaxMatches - 1; i > 0; i-- ) + { + Match[ i ] = Match[ i - 1 ]; + } + + Match[ 0 ].Len = Len; + Match[ 0 ].Dist = Cur - Str; + + if( NumMatches < Comp.MaxMatches ) NumMatches++; + + if( Len == Max ) break; + } + } + + if( !--More ) break; + + Rover = Comp.MatchChain[ Rover ]; + } + } + + return( NumMatches ); + } + + +int Comp_FindLongestMatches( MATCH Match[ MAX_MATCHES ] ) + { + int i, j, Num; + + Num = New_Comp_FindLongestMatches( Match ); + + if( Num == 0 || Num == Comp.MaxMatches ) return( Num ); + + // OK, here's a trick. If there are fewer than Comp.MaxMatches matches try + // to synthesize shorter matches by copying matches and reducing their + // lengths by one. + + for( i = 0; i < Num - 1 && Num < Comp.MaxMatches; i++ ) + { + if( Match[ i ].Len > Match[ i + 1 ].Len + 1 ) + { + if( Match[ i ].Dist <= Comp.ValidDistances[ Match[ i ].Len - 1 ] ) + { + // Move the other matches down + for( j = Num; j > i; j-- ) + { + Match[ j ] = Match[ j - 1 ]; + } + + Match[ i + 1 ].Len--; + + Num++; + } + } + } + + for( i = 1; i < Comp.MaxMatches; i++ ) + { + if( i == Num ) + { + Match[ i ] = Match[ i - 1 ]; + + Match[ i ].Len--; + + if( Match[ i ].Dist <= Comp.ValidDistances[ Match[ i ].Len ] ) + { + Num++; + } + } + } + + return( Num ); + } + + +void Comp_Advance( int NumBytes ) + { + Comp.Compressed += NumBytes; + + while( NumBytes-- ) + { + Comp.MatchChain[ Comp.Cur ] = Comp.MatchTable[ Comp.HashVal ]; + + #ifdef DEBUG + if( Comp.MatchChain[ Comp.Cur ] >= Comp.Cur ) + { + puts( "ERR: Advance." ); + exit( -1 ); + } + #endif + + Comp.MatchTable[ Comp.HashVal ] = Comp.Cur; + + Comp.Cur++; + + HASH_UPDATE( Comp.Buf[ Comp.Cur + 2 ] ); + } + + if( Comp.Cur > Comp.WindowSize ) + { + Comp.Left = Comp.Cur - Comp.WindowSize; + } + else + { + Comp.Left = 0; + } + +//BUGBUG 25-May-94 msliger (fixed) needed buffer refill before WindowSize +// So I moved the refill call down to make it not conditional on +// Cur > WindowSize. +//BUGBUG 25-May-94 msliger (fixed) Source data overshoot +// This code must limit itself to the amount of data currently in +// the window. The size of the buffer is immaterial at this point. + if (( Comp.Cur >= Comp.Right - MATCH_MAX ) //msliger + && (!Comp.ReachedEOF)) //msliger no needless memmove + { //msliger + Comp_FillBuf(); //msliger + } //msliger + } + + +int Comp_FindBetterMatch( int OldMatchLen ) + { + MATCH Match; + WORD OldHashVal = Comp.HashVal; + + Comp.Cur++; + + HASH_UPDATE( Comp.Buf[ Comp.Cur + 2 ] ); + + Comp_FindLongestMatch( &Match ); + + Comp.Cur--; + Comp.HashVal = OldHashVal; + + return( Match.Len > OldMatchLen ); + } + + +typedef double CostType; + + +typedef struct + { + CostType Cost; // cost, in bits (scaled if an int) + short int Literal; // -1 if it's really a match + MATCH Match; // if it's a literal then Match.Len == 1 + } MOVE; + + +typedef struct + { + short int Best; // best move + short int Cur; // current move being tried, may become Best + short int NumMoves; // number of moves, 1 to MAX_MOVES + MOVE Moves[ MAX_MOVES ]; + } OPT_ITEM; + +#define TREE_SIZE (MATCH_MAX * 5) + +struct + { + int Len; // number of items + #ifdef DEBUG + int NumSolutions; + #endif + CostType TotalCost[ TREE_SIZE + 1 ]; + OPT_ITEM List[ TREE_SIZE + 1 ]; + } Opt; + + +void Search( int Node, CostType CostSoFar ) + { + int i; + + if( Node >= Opt.Len ) // finished? + { + #ifdef DEBUG + if( Node > Opt.Len ) + { + puts( "ERR: Exceeded table size limit." ); + exit( -1 ); + } + #endif + + if( CostSoFar < Opt.TotalCost[ Node ] ) + { + #ifdef DEBUG + Opt.NumSolutions++; + #endif + + Opt.TotalCost[ Node ] = CostSoFar; + + // save the best path + + for( i = 1; i < Opt.Len; i++ ) + { + Opt.List[ i ].Best = -1; + } + + for( i = 0; i < Opt.Len; ) + { +#if 0 + x = Opt.List[ i ].Cur; + + Opt.List[ i ].Best = x; + + i += Opt.List[ i ].Moves[ x ].Match.Len; +#else + i += Opt.List[ i ].Moves[ Opt.List[ i ].Best = + Opt.List[ i ].Cur ].Match.Len; +#endif + } + } + } + else + { + if( CostSoFar < Opt.TotalCost[ Node ] ) + { + if( CostSoFar < Opt.TotalCost[ Opt.Len ] ) + { + if( Opt.List[ Node ].Best >= 0 ) + { + static CostType Diff; + + // Aha! We have found an intersection into a best-path. + // Saves us a lot of time! + + // First, update the costs from the point of intersection + // to the goal. + + Diff = Opt.TotalCost[ Node ] - CostSoFar; + + for( i = Node; i < Opt.Len; ) + { + Opt.TotalCost[ i ] -= Diff; + + i += Opt.List[ i ].Moves[ Opt.List[ i ].Best ].Match.Len; + } + + Opt.TotalCost[ i ] -= Diff; // Opt.Len + + // Second, this IS the best path so eliminate any others. + + for( i = 1; i < Node; i++ ) + { + Opt.List[ i ].Best = -1; + } + + // Third, update the best path to the point of intersection. + + for( i = 0; i < Node; ) + { +#if 0 + x = Opt.List[ i ].Cur; + + Opt.List[ i ].Best = x; + + i += Opt.List[ i ].Moves[ x ].Match.Len; +#else + i += Opt.List[ i ].Moves[ Opt.List[ i ].Best = + Opt.List[ i ].Cur ].Match.Len; +#endif + } + } + else + { + Opt.TotalCost[ Node ] = CostSoFar; + + for( i = Opt.List[ Node ].NumMoves - 1; i >= 0; i-- ) // try each move + { + Opt.List[ Node ].Cur = i; + + Search( Opt.List[ Node ].Moves[ i ].Match.Len + Node, + Opt.List[ Node ].Moves[ i ].Cost + CostSoFar ); + } + } + } + } + } + } + + +// this outputs the final result + +void GenerateOutput( void ) + { + int i, Best; + + for( i = 0; i < Opt.Len; ) + { + Best = Opt.List[ i ].Best; + + #ifdef DEBUG + if( Best < 0 || Best >= MAX_MOVES ) + { + puts( "ERR: 'Best' range." ); + exit( -1 ); + } + #endif + + if( Opt.List[ i ].Moves[ Best ].Literal < 0 ) + { + Lz_Encode_Match( &Opt.List[ i ].Moves[ Best ].Match ); + } + else + { + Lz_Encode_Literal( Opt.List[ i ].Moves[ Best ].Literal ); + } + + i += Opt.List[ i ].Moves[ Best ].Match.Len; + } + + #ifdef DEBUG + if( i != Opt.Len ) + { + puts( "ERR: Misaligned output." ); + exit( -1 ); + } + #endif + } + + +void Optimize( void ) + { + int i; + CostType Total; + + Total = 0.1; + + Opt.TotalCost[ 0 ] = Total; + + for( i = 0; i < Opt.Len; i++ ) + { + Opt.List[ i ].Best = -1; + + Total += Opt.List[ i ].Moves[ 0 ].Cost; // assume a simple literal + + Opt.TotalCost[ i + 1 ] = Total; + } + + #ifdef DEBUG + Opt.NumSolutions = 0; + #endif + + Search( 0, 0 ); + + #ifdef DEBUG + if( Opt.NumSolutions == 0 ) + { + puts( "ERR: No solution." ); + exit( -1 ); + } + #endif + + GenerateOutput(); + } + + +// Compresses a file. + +void Comp_Compress_Slow( void ) + { + int i, j, Len, Cut, NumMatches; + MATCH Match[ MAX_MATCHES ]; +#ifndef PAQLIB //msliger + unsigned Counter = 1024; +#endif //msliger + LONGDOUBLE DeltaCost = 0.0; + + while( Comp.Compressed < Comp.FileSize ) + { + Cut = 1; + Opt.Len = 0; + + while( Cut ) + { +#ifndef PAQLIB //msliger + if( --Counter == 0 ) // Reassure the user that + { // something is really happening. + Counter = 2048; + + if( Comp.ShowStatus ) + { + printf( "\b\b\b\b\b%4.1f%%", + Comp.Compressed * 99.9 / (double)Comp.FileSize ); + } + } +#endif //msliger + + Cut--; + + Opt.List[ Opt.Len ].NumMoves = 1; + + Opt.List[ Opt.Len ].Moves[ 0 ].Literal = Comp.Buf[ Comp.Cur ]; + Opt.List[ Opt.Len ].Moves[ 0 ].Match.Len = 1; + Opt.List[ Opt.Len ].Moves[ 0 ].Cost = + Lz_Encode_Literal_Cost( Comp.Buf[ Comp.Cur ] ) + DeltaCost; + + if( (NumMatches = Comp_FindLongestMatches( Match )) != 0 ) + { + Opt.List[ Opt.Len ].NumMoves = NumMatches + 1; + + i = 0; + + do + { + Opt.List[ Opt.Len ].Moves[ NumMatches ].Literal = -1; + Opt.List[ Opt.Len ].Moves[ NumMatches ].Match = Match[ i ]; + Opt.List[ Opt.Len ].Moves[ NumMatches ].Cost = + Lz_Encode_Match_Cost( &Match[ i ] ) + DeltaCost; + + i++; + } + while( --NumMatches ); + + if( Match[ 0 ].Len > Cut ) Cut = Match[ 0 ].Len - 1; + } + + Opt.Len++; + + DeltaCost = Opt.Len / (TREE_SIZE / 4.1); + + Comp_Advance( 1 ); + + if( Opt.Len == TREE_SIZE ) + { + // We've reached the end of the list so set up an artificial + // cutting point. This degrades compression but only very + // slightly. In practice, I expect this code to execute only + // on long run-lengths which should be pretty much optimized + // anyway. + + Cut = 0; + + // We need too make sure that no matches overlap beyond + // the end of the list. + + for( i = Opt.Len - 1; i > Opt.Len - MATCH_MAX; i-- ) + { + for( j = 1; j < Opt.List[ i ].NumMoves; j++ ) + { + Len = Opt.Len - i; + + if( Opt.List[ i ].Moves[ j ].Match.Len > Len ) + { + // OK, now we have a match which is too long. + // Try to truncate it. + + if( Len < MATCH_MIN || Opt.List[ i ].Moves[ j ].Match.Dist > + Comp.ValidDistances[ Len ] ) + { + // Truncating it makes it too short so just ditch + // the whole thing by replacing it with its literal. + + // Assume the literal is stored as the first move. + + Opt.List[ i ].Moves[ j ] = Opt.List[ i ].Moves[ 0 ]; + } + else + { + Opt.List[ i ].Moves[ j ].Match.Len = Len; + } + } + } + } + } + } + + Optimize(); + } + } + + +// Compresses a file. + +void Comp_Compress_Fast( void ) + { + MATCH Match; +#ifndef PAQLIB //msliger + unsigned Counter = 2048; +#endif //msliger + + while( Comp.Compressed < Comp.FileSize ) + { +#ifndef PAQLIB //msliger + if( --Counter == 0 ) // Reassure the user that + { // something is really happening. + Counter = 4096; + + if( Comp.ShowStatus ) + { + printf( "\b\b\b\b\b%4.1f%%", + Comp.Compressed * 99.9 / (double)Comp.FileSize ); + } + } +#endif //msliger + + if( Comp_FindLongestMatch( &Match ) ) + { + if( Comp.CompressionLevel == 1 || //msliger + !Comp_FindBetterMatch( Match.Len ) ) + { + Lz_Encode_Match( &Match ); + + Comp_Advance( Match.Len ); + } + else + { + Lz_Encode_Literal( Comp.Buf[ Comp.Cur ] ); + + Comp_Advance( 1 ); + } + } + else + { + Lz_Encode_Literal( Comp.Buf[ Comp.Cur ] ); + + Comp_Advance( 1 ); + } + } + } + + +#include "bit.h" + +#ifndef PAQLIB //msliger +WORD Comp_Compress( char *FileName, long FileSize ) + { + long BitsOutput = Bit_GetTotal(); + + Comp.FileSize = FileSize; + Comp.Compressed = 0; + Comp.Checksum = 0; + Comp.TxtFile = fopen( FileName, "rb" ); + Comp.ReachedEOF = 0; //msliger + + if (setvbuf(Comp.TxtFile,NULL,_IOFBF,131072)) //msliger + { //msliger + printf("source setvbuf failed.\n"); //msliger + } //msliger + //msliger + Comp_FillBuf(); + +//BUGBUG 25-May-94 msliger trouble for size < 3 ? + HASH_UPDATE( Comp.Buf[ 0 ] ); + HASH_UPDATE( Comp.Buf[ 1 ] ); + HASH_UPDATE( Comp.Buf[ 2 ] ); + + if( Comp.ShowStatus ) + { + printf( " 0.0%%" ); //BUGBUG 24-May-94 msliger (fixed) "%" to "%%" + } + + switch( Comp.CompressionLevel ) //msliger + { + case 1: + Comp.MatchFutilityCutoff = 5; + Comp_Compress_Fast(); + break; + case 2: + Comp.MatchFutilityCutoff = 50; + Comp_Compress_Fast(); + break; + case 3: + Comp.MatchFutilityCutoff = 250; + Comp_Compress_Fast(); + break; + case 4: + Comp.MatchFutilityCutoff = 100; + Comp.MaxMatches = MAX_MATCHES - 3; + Comp_Compress_Slow(); + break; + case 5: + Comp.MatchFutilityCutoff = 200; + Comp.MaxMatches = MAX_MATCHES - 2; + Comp_Compress_Slow(); + break; + case 6: + Comp.MatchFutilityCutoff = 400; + Comp.MaxMatches = MAX_MATCHES - 1; + Comp_Compress_Slow(); + break; + case 7: + Comp.MatchFutilityCutoff = 1000; + Comp.MaxMatches = MAX_MATCHES; + Comp_Compress_Slow(); + } + + fclose( Comp.TxtFile ); + + if( Comp.ShowStatus ) + { + printf( "\b\b\b\b\b\b100.0%%" ); + } + + BitsOutput = Bit_GetTotal() - BitsOutput; + + if( BitsOutput == 0 ) BitsOutput = 8; + if( FileSize == 0 ) FileSize = 1; + + printf( " %5.1f%%", ((double)BitsOutput / (double)FileSize) * (100.0/8.0) ); + + return( Comp.Checksum ); + } +#endif /* not PAQLIB */ //msliger + + +#ifdef PAQLIB //msliger (entire function) + +int cbTarget; // public (to BIT.C) +char *pbTarget; +int fTargetOverflow; +int cbResult; + +WORD Comp_CompressBlock( void *pbSource, UINT cbSource, + void *pbDest, UINT cbDest, UINT *pcbResult ) + { + Comp.FileSize = cbSource; + Comp.Compressed = 0; + Comp.pbSource = pbSource; + Comp.cbSource = cbSource; + Comp.ReachedEOF = 0; //msliger + + cbTarget = cbDest; // hack these along to BIT.C + pbTarget = pbDest; + fTargetOverflow = 0; + cbResult = 0; + + Arith_Init(); // start each block clean + + Comp_FillBuf(); + +//BUGBUG 25-May-94 msliger trouble for size < 3 ? + HASH_UPDATE( Comp.Buf[ 0 ] ); + HASH_UPDATE( Comp.Buf[ 1 ] ); + HASH_UPDATE( Comp.Buf[ 2 ] ); + + switch( Comp.CompressionLevel ) //msliger + { + case 1: + Comp.MatchFutilityCutoff = 5; + Comp_Compress_Fast(); + break; + case 2: + Comp.MatchFutilityCutoff = 50; + Comp_Compress_Fast(); + break; + case 3: + Comp.MatchFutilityCutoff = 250; + Comp_Compress_Fast(); + break; + case 4: + Comp.MatchFutilityCutoff = 100; + Comp.MaxMatches = MAX_MATCHES - 3; + Comp_Compress_Slow(); + break; + case 5: + Comp.MatchFutilityCutoff = 200; + Comp.MaxMatches = MAX_MATCHES - 2; + Comp_Compress_Slow(); + break; + case 6: + Comp.MatchFutilityCutoff = 400; + Comp.MaxMatches = MAX_MATCHES - 1; + Comp_Compress_Slow(); + break; + case 7: + Comp.MatchFutilityCutoff = 1000; + Comp.MaxMatches = MAX_MATCHES; + Comp_Compress_Slow(); + } + + Arith_Close(); // flush out remaining bits + + *pcbResult = cbResult; + + return( fTargetOverflow ); + } +#endif /* PAQLIB */ //msliger + + +void Comp_Close( void ) + { + Lz_Close(); + + Rtl_Free( Comp.MatchTable ); + Rtl_Free( Comp.MatchChain ); /* Comp.Buf goes with it */ + } + + +#ifdef PAQLIB //msliger (entire function) + +// this code is only suitable for use with Comp_CompressBlock() + +void Comp_Reset( void ) + { + int i; + + Lz_Close(); + Lz_Init( Comp.WindowBits ); + +//BUGBUG 25-May-94 msliger Init of hash tables should be done with memset + for( i = 0; i < HASH_TABLE_SIZE; i++ ) + { + Comp.MatchTable[ i ] = -1; + } + + for( i = 0; i < Comp.BufSize; i++ ) + { + Comp.MatchChain[ i ] = -1; + } + + Comp.Left = Comp.Cur = Comp.Right = 0; + } +#endif /* PAQLIB */ //msliger diff --git a/private/windows/diamond/quantum/comp.h b/private/windows/diamond/quantum/comp.h new file mode 100644 index 000000000..0226e40fd --- /dev/null +++ b/private/windows/diamond/quantum/comp.h @@ -0,0 +1,28 @@ +// COMP.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __COMP +#define __COMP + +#include <stdio.h> +#include "defs.h" + +void Comp_Init( BYTE WindowBits, int CompressionLevel ); //msliger +void Comp_Close( void ); + +#ifndef PAQLIB //msliger +WORD Comp_Compress( char *FileName, long FileSize ); +#endif //msliger + +#ifdef PAQLIB //msliger +WORD Comp_CompressBlock( void *pbSrc, UINT cbSrc, //msliger + void *pbDst, UINT cbDst, UINT *pcbResult ); //msliger +void Comp_Reset( void ); //msliger +#endif //msliger + +#endif // comp.h diff --git a/private/windows/diamond/quantum/crc32.h b/private/windows/diamond/quantum/crc32.h new file mode 100644 index 000000000..25da8ce55 --- /dev/null +++ b/private/windows/diamond/quantum/crc32.h @@ -0,0 +1,4 @@ +extern void CRC32Reset(void); +extern void CRC32Restore(long); +extern void CRC32Update(unsigned char *,unsigned); +extern long CRC32Result(void); diff --git a/private/windows/diamond/quantum/dcomp.c b/private/windows/diamond/quantum/dcomp.c new file mode 100644 index 000000000..6985c09c5 --- /dev/null +++ b/private/windows/diamond/quantum/dcomp.c @@ -0,0 +1,764 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * Copyright (c) 1993,1994 Cinematronics + * All Rights Reserved. + * + * DCOMP.C: decompressor core + * + * History: + * 08-Jul-1994 msliger Added disk-based ring buffer support. + * 14-Jul-1994 msliger Fixed build of UNPAQ app. + * 30-Aug-1994 msliger Fixed marking buffer dirty if newest. + * 01-Feb-1995 msliger Fixed reporting ring init failures. + */ + +// DCOMP.C +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 Cinematronics +// All rights reserved. +// +// This file contains trade secrets of Cinematronics. +// Do NOT distribute! + +#include <stdio.h> +#include <stdlib.h> + +#ifdef STAFFORD +#include <alloc.h> +#endif // STAFFORD + +#include "quantum.h" + +#ifndef UNPAQLIB //msliger +#include "arc.h" +#endif //msliger + +#include "dcomp.h" +#include "lz.h" +#include "rtl.h" + +#ifndef UNPAQLIB //msliger +extern ARC Arc; +#endif //msliger + +#ifdef UNPAQLIB //msliger +#include "arith.h" +#endif + +#ifdef UNPAQLIB +#define DISK_RING /* enable DISK_RING support code */ +#endif + +#ifdef DISK_RING +#include <fcntl.h> /* for disk ring buffer code */ +#include <sys\stat.h> + +#include "qdi.h" /* for RINGNAME, file functions */ +#include "qdi_int.h" /* lastContext */ +#endif //msliger + + +typedef struct + { + BYTE HUGE *Buf; // history buffer: NULL -> using disk ring buffer + BYTE HUGE *BufEnd; // last byte in history buffer + 1 + + BYTE HUGE *CurPtr; // pointer to oldest byte in history buffer + unsigned long Cur; // current position in the history buffer + long WindowMask; // used to normalize Cur for wrapping + long WindowSize; // size of the history window +#ifndef UNPAQLIB //msliger + long NumBytes; // total number of bytes to decompress + FILE *OutFile; // the output file + WORD Checksum; +#else //msliger + unsigned short NumBytes; // total number of bytes to decompress + BYTE *OutBuffer; //msliger the output buffer + int fOutOverflow; //msliger if too little space in output buffer + BYTE WindowBits; //msliger needed in DComp_Reset() + int fRingFault; // if disk callbacks fail +#endif //msliger + } DCOMP; + + +DCOMP DComp; + +void (FAST2 NEAR *DComp_Token_Match)( MATCH Match ); +void (FAST2 NEAR *DComp_Token_Literal)( int Chr ); + +static void FAST2 DComp_Internal_Match( MATCH Match ); +static void FAST2 DComp_Internal_Literal( int Chr ); + + +#ifdef DISK_RING /* virtual ring manager */ + +#define BUFFER_SIZE (4096) /* must be 2^Nth */ + +#define MIN_BUFFERS 3 /* minimum number we want */ + + +typedef struct aBuffer +{ + struct aBuffer FAR *pLinkNewer; /* link to more recently used */ + struct aBuffer FAR *pLinkOlder; /* link to less recently used */ + int BufferPage; /* what page this is, -1 -> invalid */ + int BufferDirty; /* NZ -> needs to be written */ + BYTE Buffer[BUFFER_SIZE]; /* content */ +} BUFFER, FAR *PBUFFER; + + +typedef struct +{ + PBUFFER pBuffer; /* pointer to buffer, NULL if not present */ + int fDiskValid; /* NZ -> this page has been written to disk */ +} PAGETABLEENTRY; + + +static struct +{ + int Handle; /* ring file handle */ + PBUFFER RingBuffer; /* current output ring buffer */ + BYTE FAR *RingPointer; /* current output pointer (into RingBuffer) */ + BYTE FAR *RingPointerLimit; /* address of last byte of RingBuffer + 1 */ + int RingPages; /* how many pages there are total */ + PBUFFER pNewest; /* pointer to most recently used buffer */ + PBUFFER pOldest; /* pointer to least recently used buffer */ + PAGETABLEENTRY FAR *PageTable; /* pointer to array of pointers */ +} Disk; + + +static int FAST DComp_Ring_Init(void); +static void FAST DComp_Ring_Reset(void); +static PBUFFER FAST DComp_Ring_Load(int page,int fWrite); +static void FAST2 DComp_Ring_Match( MATCH Match ); +static void FAST2 DComp_Ring_Literal( int Chr ); +static void FAST DComp_Ring_Close(void); + +#endif /* DISK_RING */ + + +int FAST DComp_Init( BYTE WindowBits ) //msliger + { + DComp.WindowSize = 1L << WindowBits; //msliger + DComp.WindowMask = DComp.WindowSize - 1; + DComp.Cur = 0; + +#ifdef UNPAQLIB //msliger + DComp.fRingFault = 0; + DComp.WindowBits = WindowBits; //msliger +#endif //msliger + + if( (DComp.Buf = Rtl_Malloc( DComp.WindowSize )) != NULL ) + { + DComp_Token_Match = DComp_Internal_Match; /* use internal buffering */ + DComp_Token_Literal = DComp_Internal_Literal; + + DComp.CurPtr = DComp.Buf; + DComp.BufEnd = DComp.Buf + DComp.WindowSize; + } +#ifdef DISK_RING + else if (DComp_Ring_Init()) /* try disk ring buffer */ + { + DComp_Token_Match = DComp_Ring_Match; /* use disk buffering */ + DComp_Token_Literal = DComp_Ring_Literal; + } +#endif + else + { + return(1); /* if can't create ring buffer */ + } + + Lz_Init( WindowBits ); //msliger + + return(0); + } + + +void FAST2 DComp_Internal_Match( MATCH Match ) + { + BYTE HUGE *SrcPtr; + register BYTE Chr; + + if (DComp.NumBytes >= (unsigned) Match.Len) + { + SrcPtr = DComp.Buf + ((DComp.Cur - Match.Dist) & DComp.WindowMask); + + DComp.NumBytes -= Match.Len; + DComp.Cur += Match.Len; + + while (Match.Len--) + { +#ifdef UNPAQLIB + Chr = *SrcPtr; + + *DComp.CurPtr = Chr; + + *DComp.OutBuffer++ = Chr; +#else + Chr = *SrcPtr; + + *DComp.CurPtr = Chr; + + putc( Chr, DComp.OutFile ); + + DComp.Checksum ^= Chr; + + if( DComp.Checksum & 0x8000 ) // simulate a ROL in C, yucky! + { + DComp.Checksum <<= 1; + DComp.Checksum++; + } + else + { + DComp.Checksum <<= 1; + } +#endif + + if (++SrcPtr == DComp.BufEnd) + { + SrcPtr = DComp.Buf; + } + + if (++DComp.CurPtr == DComp.BufEnd) + { + DComp.CurPtr = DComp.Buf; + } + } + } + else /* match too large to fit */ + { + DComp.NumBytes = 0; +#ifdef UNPAQLIB + DComp.fOutOverflow = 1; +#endif + } + } + + +void FAST2 DComp_Internal_Literal( int Chr ) + { + DComp.NumBytes--; + DComp.Cur++; + +#ifndef UNPAQLIB + putc( (BYTE) Chr, DComp.OutFile ); + + DComp.Checksum ^= (BYTE) Chr; + + if( DComp.Checksum & 0x8000 ) // simulate a ROL in C, yucky! + { + DComp.Checksum <<= 1; + DComp.Checksum++; + } + else + { + DComp.Checksum <<= 1; + } +#else + *DComp.OutBuffer++ = (BYTE) Chr; +#endif + + *DComp.CurPtr = (BYTE) Chr; + + if (++DComp.CurPtr == DComp.BufEnd) + { + DComp.CurPtr = DComp.Buf; + } + } + + +#ifndef UNPAQLIB +WORD FAST DComp_Decompress( FILE *OutputFile, long NumBytes ) + { + DComp.OutFile = OutputFile; + DComp.NumBytes = NumBytes; + DComp.Checksum = 0; + + { + while( DComp.NumBytes ) + { + Lz_NextToken(); + } + } + + if( DComp.NumBytes < 0 ) + { + puts( "ERR: Incorrect file size." ); + exit( -1 ); + } + + return( DComp.Checksum ); + } +#endif /* not UNPAQLIB */ //msliger + +#ifdef UNPAQLIB + +char *pbSource; +int cbSource; +int fSourceOverflow; + +WORD FAST DComp_DecompressBlock( void *pbSrc,UINT cbSrc,void *pbDst,UINT cbDst ) + { + DComp.NumBytes = cbDst; + DComp.OutBuffer = pbDst; + DComp.fOutOverflow = 0; + + pbSource = pbSrc; // pass these along to BIT.C + cbSource = cbSrc; + fSourceOverflow = 0; + + Arith_Init(); + + while( (DComp.NumBytes) && (!fSourceOverflow) ) + { + Lz_NextToken(); + } + + Arith_Close(); + + return( fSourceOverflow || DComp.fOutOverflow || DComp.fRingFault); + } +#endif + + +void FAST DComp_Close( void ) + { +#ifdef DISK_RING + if (DComp.Buf == NULL) + { + DComp_Ring_Close(); /* if using a disk-based ring buffer */ + } + else + { + Rtl_Free( DComp.Buf ); /* if using memory-based ring buffer */ + } +#else + Rtl_Free( DComp.Buf ); /* always memory-based */ +#endif + + Lz_Close(); + } + + +#ifdef UNPAQLIB + +// this code is only for use with DComp_DecompressBlock() + +void FAST DComp_Reset( void ) + { + Lz_Close(); + + DComp.Cur = 0; + DComp.CurPtr = DComp.Buf; + DComp.fRingFault = 0; + +#ifdef DISK_RING + if (DComp.Buf == NULL) + { + DComp_Ring_Reset(); + } +#endif + + Lz_Init( DComp.WindowBits ); + } +#endif /* UNPAQLIB */ + + +#ifdef DISK_RING + +// disk-based ring buffer support code + +static int FAST DComp_Ring_Init(void) +{ + RINGNAME ringName; + PBUFFER pBuffer; + int cBuffers; + + if (lastContext->pfnOpen == NULL) + { + return(0); /* failed, no disk services */ + } + + ringName.wildName[0] = '*'; + ringName.wildName[1] = '\0'; + ringName.fileSize = DComp.WindowSize; + + Disk.Handle = lastContext->pfnOpen((char FAR *) &ringName, + (_O_BINARY|_O_RDWR|_O_CREAT),(_S_IREAD|_S_IWRITE)); + + if (Disk.Handle == -1) + { + return(0); /* failed, can't make disk file */ + } + + Disk.RingPages = (int) (DComp.WindowSize / BUFFER_SIZE); + if (Disk.RingPages < MIN_BUFFERS) + { + Disk.RingPages = MIN_BUFFERS; /* if DComp.WindowSize < BUFFER_SIZE */ + } + + Disk.PageTable = Rtl_Malloc(sizeof(PAGETABLEENTRY) * Disk.RingPages); + if (Disk.PageTable == NULL) + { + lastContext->pfnClose(Disk.Handle); /* close the file */ + + return(0); /* failed, can't get page table */ + } + + Disk.pNewest = NULL; + + /* DComp_Ring_Close() can be used to abort from this point on */ + + for (cBuffers = 0; cBuffers < Disk.RingPages; cBuffers++) + { + pBuffer = Rtl_Malloc(sizeof(BUFFER)); + + if (pBuffer != NULL) + { + pBuffer->pLinkNewer = NULL; /* none are newer */ + pBuffer->pLinkOlder = Disk.pNewest; /* all the others older now */ + + if (Disk.pNewest != NULL) + { + Disk.pNewest->pLinkNewer = pBuffer; /* old guy now knows about new */ + } + else /* if nobody else */ + { + Disk.pOldest = pBuffer; /* guess I'm the oldest too */ + } + + Disk.pNewest = pBuffer; /* I'm the newest */ + } + else /* if pBuffer == NULL */ + { + if (cBuffers < MIN_BUFFERS) /* less than minimum? */ + { + DComp_Ring_Close(); /* give it up */ + + return(0); /* failed, can't get min buffers */ + } + else /* if we got the minimum */ + { + break; /* got enough, quit trying */ + } + } + } + + DComp_Ring_Reset(); /* init everything else */ + + return(1); /* ring buffer created */ +} + + +static void FAST DComp_Ring_Reset(void) +{ + PBUFFER walker; + int iPage; + + for (walker = Disk.pNewest; walker != NULL; walker = walker->pLinkOlder) + { + walker->BufferPage = -1; /* buffer is not valid */ + walker->BufferDirty = 0; /* and doesn't need writing */ + } + + for (iPage = 0; iPage < Disk.RingPages; iPage++) + { + Disk.PageTable[iPage].pBuffer = NULL; /* not in memory */ + Disk.PageTable[iPage].fDiskValid = 0; /* not on disk */ + } + + Disk.RingBuffer = DComp_Ring_Load(0,1); /* make a page 0 for writing */ + + if (Disk.RingBuffer != NULL) /* always will pass */ + { + Disk.RingPointer = Disk.RingBuffer->Buffer; + Disk.RingPointerLimit = Disk.RingPointer + BUFFER_SIZE; + } +} + + +static void FAST2 DComp_Ring_Match( MATCH Match ) +{ + long SrcOffset; /* offset into output ring */ + int SrcPage; /* page # where that offset lies */ + int Chunk; /* number of bytes this pass */ + BYTE FAR *SrcPtr; /* pointer to source bytes */ + BYTE Chr; /* data character */ + PBUFFER SrcBuffer; /* buffer where source data is */ + int SrcBufferOffset; /* offset within the buffer */ + int newPage; + + if (DComp.NumBytes >= (unsigned) Match.Len) + { + SrcOffset = (DComp.Cur - Match.Dist) & DComp.WindowMask; + DComp.NumBytes -= Match.Len; + DComp.Cur += Match.Len; + + /* promote current output page to make sure it's never discarded */ + + if (DComp_Ring_Load(Disk.RingBuffer->BufferPage,0) == NULL) + { + DComp.NumBytes = 0; + + DComp.fRingFault = 1; + + return; + } + + while (Match.Len) + { + /* Limit: number of bytes requested */ + + Chunk = Match.Len; /* try for everything */ + + /* Limit: space remaining on current output page */ + + if ((Disk.RingPointerLimit - Disk.RingPointer) < Chunk) + { + Chunk = Disk.RingPointerLimit - Disk.RingPointer; + } + + SrcPage = (int) (SrcOffset / BUFFER_SIZE); + SrcBufferOffset = (int) (SrcOffset % BUFFER_SIZE); + + SrcBuffer = DComp_Ring_Load(SrcPage,0); /* for reading */ + if (SrcBuffer == NULL) + { + DComp.NumBytes = 0; + + DComp.fRingFault = 1; + + return; + } + + SrcPtr = SrcBuffer->Buffer + SrcBufferOffset; + + /* Limit: number of source bytes on current output page */ + + if ((BUFFER_SIZE - SrcBufferOffset) < Chunk) + { + Chunk = (BUFFER_SIZE - SrcBufferOffset); + } + + SrcOffset += Chunk; + SrcOffset &= DComp.WindowMask; + Match.Len -= Chunk; + + while (Chunk--) /* copy this chunk */ + { + Chr = *SrcPtr++; + + *Disk.RingPointer++ = Chr; + + *DComp.OutBuffer++ = Chr; + } + + if (Disk.RingPointer == Disk.RingPointerLimit) + { + newPage = (Disk.RingBuffer->BufferPage + 1); + if (newPage >= Disk.RingPages) + { + newPage = 0; + } + + Disk.RingBuffer = DComp_Ring_Load(newPage,1); /* for writing */ + if (Disk.RingBuffer == NULL) + { + DComp.NumBytes = 0; + + DComp.fRingFault = 1; + + return; + } + + Disk.RingPointer = Disk.RingBuffer->Buffer; + Disk.RingPointerLimit = Disk.RingPointer + BUFFER_SIZE; + } + } /* while Match.Len */ + } /* if Match.Len size OK */ + else /* match too large to fit */ + { + DComp.NumBytes = 0; + + DComp.fOutOverflow = 1; + } +} + + +static void FAST2 DComp_Ring_Literal( int Chr ) +{ + int newPage; + + DComp.NumBytes--; + DComp.Cur++; + + *DComp.OutBuffer++ = (BYTE) Chr; + + *Disk.RingPointer = (BYTE) Chr; + + if (++Disk.RingPointer == Disk.RingPointerLimit) + { + newPage = (Disk.RingBuffer->BufferPage + 1); + if (newPage >= Disk.RingPages) + { + newPage = 0; + } + + Disk.RingBuffer = DComp_Ring_Load(newPage,1); /* for writing */ + if (Disk.RingBuffer == NULL) + { + DComp.NumBytes = 0; + + DComp.fRingFault = 1; + + return; + } + + Disk.RingPointer = Disk.RingBuffer->Buffer; + Disk.RingPointerLimit = Disk.RingPointer + BUFFER_SIZE; + } +} + + +/* Bring page into a buffer, return a pointer to that buffer. fWrite */ +/* indicates the caller's intentions for this buffer, NZ->consider it */ +/* dirty now. Returns NULL if there is a paging fault (callback */ +/* failed) or if any internal assertions fail. */ + +static PBUFFER FAST DComp_Ring_Load(int page,int fWrite) +{ + PBUFFER pBuffer; + long iPagefileOffset; + + pBuffer = Disk.PageTable[page].pBuffer; /* look up this page */ + + if (pBuffer != NULL) /* if it's in the table */ + { + if (pBuffer != Disk.pNewest) /* promote if not newest */ + { + pBuffer->pLinkNewer->pLinkOlder = pBuffer->pLinkOlder; + + if (pBuffer->pLinkOlder != NULL) /* if there is someone older */ + { + pBuffer->pLinkOlder->pLinkNewer = pBuffer->pLinkNewer; + } + else + { + Disk.pOldest = pBuffer->pLinkNewer; + } + + /* link into head of chain */ + + Disk.pNewest->pLinkNewer = pBuffer; /* newest now knows one newer */ + pBuffer->pLinkNewer = NULL; /* nobody's newer */ + pBuffer->pLinkOlder = Disk.pNewest; /* everybody's older */ + Disk.pNewest = pBuffer; /* I'm the newest */ + } + + pBuffer->BufferDirty |= fWrite; /* might already be dirty */ + + return(pBuffer); /* return my handle */ + } + + /* desired page is not in the table; discard oldest & use it */ + + /* assertion check: current output buffer appears to be the oldest */ + + if (Disk.pOldest == Disk.RingBuffer) + { + return(NULL); + } + + pBuffer = Disk.pOldest; /* choose the oldest buffer */ + + if (pBuffer->BufferPage != -1) /* take it out of page table */ + { + Disk.PageTable[pBuffer->BufferPage].pBuffer = NULL; /* not here now */ + + if (pBuffer->BufferDirty) /* write on eject, if dirty */ + { + iPagefileOffset = (long) pBuffer->BufferPage * BUFFER_SIZE; + + if (lastContext->pfnSeek(Disk.Handle,iPagefileOffset,SEEK_SET) != + iPagefileOffset) + { + return(NULL); + } + + if (lastContext->pfnWrite(Disk.Handle,pBuffer->Buffer,BUFFER_SIZE) != + BUFFER_SIZE) + { + return(NULL); + } + + Disk.PageTable[pBuffer->BufferPage].fDiskValid = 1; + } + } + + Disk.pOldest = Disk.pOldest->pLinkNewer; /* newer is now oldest */ + Disk.pOldest->pLinkOlder = NULL; /* oldest knows none older */ + + Disk.pNewest->pLinkNewer = pBuffer; + pBuffer->pLinkNewer = NULL; /* link into head of chain */ + pBuffer->pLinkOlder = Disk.pNewest; + Disk.pNewest = pBuffer; + + /* add new buffer to paging table */ + + Disk.PageTable[page].pBuffer = pBuffer; /* add new to paging table */ + + /* if this disk page is valid, load it */ + + if (Disk.PageTable[page].fDiskValid) + { + iPagefileOffset = (long) page * BUFFER_SIZE; + + if (lastContext->pfnSeek(Disk.Handle,iPagefileOffset,SEEK_SET) != + iPagefileOffset) + { + return(NULL); + } + + if (lastContext->pfnRead(Disk.Handle,pBuffer->Buffer,BUFFER_SIZE) != + BUFFER_SIZE) + { + return(NULL); + } + } + else if (!fWrite) + { + /* assertion failure, trying to load a never-written page from disk */ + + return(NULL); + } + + pBuffer->BufferDirty = fWrite; /* might be dirty now */ + pBuffer->BufferPage = page; /* our new page number */ + + return(pBuffer); /* return new handle */ +} + + +static void FAST DComp_Ring_Close(void) +{ + PBUFFER pBuffer, pNext; /* buffer walk pointer */ + + Rtl_Free(Disk.PageTable); /* discard page table */ + + pBuffer = Disk.pNewest; + + while (pBuffer != NULL) /* discard buffer chain */ + { + pNext = pBuffer->pLinkOlder; + Rtl_Free(pBuffer); + pBuffer = pNext; + } + + lastContext->pfnClose(Disk.Handle); /* close that file (and delete) */ +} + +#endif /* DISK_RING */ diff --git a/private/windows/diamond/quantum/dcomp.h b/private/windows/diamond/quantum/dcomp.h new file mode 100644 index 000000000..9877cfb64 --- /dev/null +++ b/private/windows/diamond/quantum/dcomp.h @@ -0,0 +1,32 @@ +// DCOMP.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __DCOMP +#define __DCOMP + +#include <stdio.h> +#include "defs.h" //msliger +#include "lz.h" + +int FAST DComp_Init( BYTE WindowBits ); //msliger +void FAST DComp_Close( void ); + +#ifndef UNPAQLIB //msliger +WORD FAST DComp_Decompress( FILE *OutputFile, long NumBytes ); +#endif //msliger + +#ifdef UNPAQLIB //msliger +WORD FAST DComp_DecompressBlock( void FAR *pbSrc, UINT cbSrc, //msliger + void FAR *pbDst, UINT cbDst ); //msliger +void FAST DComp_Reset( void ); //msliger +#endif //msliger + +extern void (FAST2 *DComp_Token_Match)( MATCH Match ); +extern void (FAST2 *DComp_Token_Literal)( int Chr ); + +#endif // dcomp.h diff --git a/private/windows/diamond/quantum/decomp.h b/private/windows/diamond/quantum/decomp.h new file mode 100644 index 000000000..fbeab0ab9 --- /dev/null +++ b/private/windows/diamond/quantum/decomp.h @@ -0,0 +1,30 @@ +/* + * Microsoft Confidential + * Copyright (c) 1994 Microsoft Corporation + * Copyright (c) 1993,1994 Cinematronics + * Copyright (c) 1993,1994 David Stafford + * All Rights Reserved. + * + * Quantum file archiver and compressor + * Advanced data compression + * + * Copyright (c) 1993,1994 David Stafford + * All rights reserved. + * + * This file contains trade secrets of Cinematronics. + * Do NOT distribute! + */ + +/* + * DECOMP.H: QDI core header + * + * History: + * 09-Jul-1994 msliger Built from DCOMP.H + * 18-Aug-1994 msliger Renamed these to allow both compressors. + */ + +extern int FAST DComp386_Init( BYTE WindowBits ); +extern UINT FAST DComp386_DecompressBlock( void FAR *pbSrc, UINT cbSrc, + void FAR *pbDst, UINT cbDst ); +extern void FAST DComp386_Reset( void ); +extern void FAST DComp386_Close( void ); diff --git a/private/windows/diamond/quantum/defs.h b/private/windows/diamond/quantum/defs.h new file mode 100644 index 000000000..4aa478a7f --- /dev/null +++ b/private/windows/diamond/quantum/defs.h @@ -0,0 +1,43 @@ +#ifndef __DEFS +#define __DEFS + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef int BOOL; +typedef unsigned int UINT; //msliger + +#ifndef NEAR +#ifdef BIT16 +#define NEAR __near +#else +#define NEAR +#endif +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR __far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE __huge +#else +#define HUGE +#endif +#endif + + +#ifdef BIT16 +#define FAST __near __pascal +#define FAST2 __near __fastcall +#else +#define FAST +#define FAST2 __fastcall +#endif + +#endif // defs.h diff --git a/private/windows/diamond/quantum/dirs b/private/windows/diamond/quantum/dirs new file mode 100644 index 000000000..65bcdc5e7 --- /dev/null +++ b/private/windows/diamond/quantum/dirs @@ -0,0 +1 @@ +DIRS=qci.nt qdi.nt diff --git a/private/windows/diamond/quantum/dirwalk.h b/private/windows/diamond/quantum/dirwalk.h new file mode 100644 index 000000000..54c34955d --- /dev/null +++ b/private/windows/diamond/quantum/dirwalk.h @@ -0,0 +1,88 @@ +/* ---------------------------------------------------------------------------------------------------------------------------- */ +/* */ +/* DIRWALK.H */ +/* */ +/* Definitions for the DirectoryWalk function found in DIRWALK.C, and the related processing calls needed. */ +/* */ +/* ---------------------------------------------------------------------------------------------------------------------------- */ + +/* int DirectoryWalk(path,Directory,File,DirectoryEnd) */ + +extern int DirectoryWalk(const char *, /* path to search, ie, "C:\" */ + int (*)(void *,const char *,const char *,void **), /* pointer to Directory() or NULL */ + int (*)(void *,const char *,const char *,unsigned,unsigned,unsigned,long), /* pointer to File() or NULL */ + int (*)(void *,const char *,const char *,void *)); /* pointer to DirectoryEnd() or NULL */ + +#define DW_MEMORY -10 /* unable to malloc() for internal use */ +#define DW_ERROR -11 /* find first/next reported an error */ +#define DW_DEPTH -12 /* path name became too long */ +#define DW_NO_ERROR 0 /* no error detected */ + +/* + User function definitions: + + The parentID and childID values are "void *"s to DirectoryWalk. They are not used internally. The user may assign + any meaning desired to the value. DirectoryWalk simply requests an assignment via the Directory(), and reports the + corresponding assignment with each file reported to File(), then to DirectoryEnd(). + + + int Directory(parentID,directory,path,childID) + void *parentID; / the handle for the directory where this directory was found / + const char *directory; / the name of the directory found, ie, "DOS" / + const char *path; / the full name of the directory, ie, "C:\DOS" / + void **childID; / Directory() assigns this directory a handle, and passes it back through here / + + If Directory() returns a non-zero value, DirectoryWalk will terminate and return that value. If NULL is passed + to DirectoryWalk() for a Directory() pointer, subdirectories will not be searched. For subdirectory searching + without specific per-directory processing, create a simple function which always returns DW_NO_ERROR (any other + return will terminate the search.) + + + int File(parentID,filename,path,attributes,filetime,filedate,filesize) + void *parentID; / the handle for the directory where this file was found / + const char *filename; / the name of the file found, ie, "ANSI.SYS" / + const char *path; / the full name of the directory where file was found, ie, "C:\DOS" / + unsigned attributes; / the file's attribute bits / + unsigned filetime; / the file's "last modification" time / + unsigned filedate; / the file's "last modification" date / + long filesize; / the file's size in bytes / + + If File() returns a value other than DW_NO_ERROR, DirectoryWalk will terminate and return that value. NULL can + be passed to DirectoryWalk() for a File() pointer; file entries found will be ignored. + + + int DirectoryEnd(parentID,directory,path,childID) + void *parentID; / the handle for the directory where this directory was found / + const char *directory; / the name of the directory found, ie, "DOS" / + const char *path; / the full name of the directory, ie, "C:\DOS" / + void *childID; / the handle assigned this directory by Directory() / + + If NULL is passed to DirectoryWalk for the DirectoryEnd() pointer, no report of end of directories will be made. + If DirectoryEnd returns a value other than DW_NO_ERROR, DirectoryWalk will terminate and return that value. + + + The attributes field bits are defined as follows: (see the #define's below) + + ---- ---- ---- ---1 file is read-only + ---- ---- ---- --1- file is hidden + ---- ---- ---- -1-- file is a "system" file + ---- ---- --1- ---- file's "modified" ("archive") bit is set + xxxx xxxx xx-x x--- undefined (should be masked by user) + + The filetime field is in the MS-DOS format: + + ---- ---- ---x xxxx seconds / 2 (0..29) + ---- -xxx xxx- ---- minutes (0..59) + xxxx x--- ---- ---- hours (0..23) + + The filedate field is in the MS-DOS format: + + ---- ---- ---x xxxx day of month (1..31) + ---- ---x xxx- ---- month (1..12) + xxxx xxx- ---- ---- year-1980 (0..127) +*/ + +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_ARCHIVE 0x0020 diff --git a/private/windows/diamond/quantum/dummy.h b/private/windows/diamond/quantum/dummy.h new file mode 100644 index 000000000..b41150f11 --- /dev/null +++ b/private/windows/diamond/quantum/dummy.h @@ -0,0 +1,46 @@ +#include "defs.h" + +#define WINAPI __stdcall __import + +#define PAGE_NOACCESS 0x01 +#define PAGE_READONLY 0x02 +#define PAGE_READWRITE 0x04 +#define PAGE_WRITECOPY 0x08 +#define PAGE_EXECUTE 0x10 +#define PAGE_EXECUTE_READ 0x20 +#define PAGE_EXECUTE_READWRITE 0x40 +#define PAGE_EXECUTE_WRITECOPY 0x80 +#define PAGE_GUARD 0x100 +#define PAGE_NOCACHE 0x200 +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 +#define MEM_FREE 0x10000 +#define MEM_PRIVATE 0x20000 +#define MEM_MAPPED 0x40000 +#define MEM_TOP_DOWN 0x100000 +#define SEC_FILE 0x800000 +#define SEC_IMAGE 0x1000000 +#define SEC_RESERVE 0x4000000 +#define SEC_COMMIT 0x8000000 +#define SEC_NOCACHE 0x10000000 +#define MEM_IMAGE SEC_IMAGE + + +void * +WINAPI +VirtualAlloc( + void *lpAddress, + DWORD dwSize, + DWORD flAllocationType, + DWORD flProtect + ); + +BOOL +WINAPI +VirtualFree( + void *lpAddress, + DWORD dwSize, + DWORD dwFreeType + ); diff --git a/private/windows/diamond/quantum/log.h b/private/windows/diamond/quantum/log.h new file mode 100644 index 000000000..373c5fda1 --- /dev/null +++ b/private/windows/diamond/quantum/log.h @@ -0,0 +1,7 @@ +#ifdef STAFFORD +#define LONGDOUBLE long double +#else +#define LONGDOUBLE double +#endif + +LONGDOUBLE MYLOG( LONGDOUBLE x ); diff --git a/private/windows/diamond/quantum/lz.c b/private/windows/diamond/quantum/lz.c new file mode 100644 index 000000000..4d91da0a0 --- /dev/null +++ b/private/windows/diamond/quantum/lz.c @@ -0,0 +1,719 @@ +// LZ.C +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 Cinematronics +// All rights reserved. +// +// This file contains trade secrets of Cinematronics. +// Do NOT distribute! + + +#ifdef STAFFORD +#define ASM asm +#else +#define ASM __asm +#endif + + +#ifndef __PCC__ + #pragma auto_inline(on) +#endif + +#include <stdlib.h> +#include "arith.h" +#include "quantum.h" //msliger +#include "lz.h" +#include "comp.h" +#include "dcomp.h" + +#ifdef __PCC__ //msliger +#include "log.h" +#endif //msliger + +typedef struct + { + unsigned Freq; + unsigned Symbol; + } COUNT; + + +typedef struct + { + int Num; // Number of symbols in this table, max of 64 +#ifdef PAQ + int Lookup[ 65 ]; // Given a symbol identifies its index +#endif + int SortCountdown; + COUNT Table[ 65 ]; // Yucky but 64+1 is the largest we ever need... + } COUNT_RECORD; + + +struct + { + COUNT_RECORD TokenType; + COUNT_RECORD Literal1; + COUNT_RECORD Literal2; + COUNT_RECORD Literal3; + COUNT_RECORD Literal4; + + COUNT_RECORD MatchCode; + + COUNT_RECORD WindowCode; + COUNT_RECORD TinyWindowCode; + COUNT_RECORD ShortWindowCode; + } Count; + + +typedef struct + { +#ifdef PAQ + unsigned MatchCodeTrans[ MATCH_NUM ]; +#endif + unsigned MatchCodeBase[ MATCH_CODES ]; + +#ifdef PAQ + unsigned WindowCodeTrans[ 2048 + (1 << (WINDOW_MAX-10)) ]; +#endif + long WindowCodeBase[ WINDOW_CODES ]; + +#ifdef DEBUG + long Stat[ 7 ]; +#endif + } LZ; + +LZ Lz; + +// Extra bits for each match length code + +unsigned MatchCodeExtra[ MATCH_CODES ] = + { 0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 }; + +// Extra bits for each distance code + +unsigned WindowCodeExtra[ WINDOW_CODES ] = + { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11, + 12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19 }; + + +extern LONGDOUBLE FAST MYLOG( LONGDOUBLE x ); + + +void FAST Lz_Init( BYTE WindowBits ) //msliger + { + int i, k; + long Base, WindowSize; + + WindowSize = 1L << WindowBits; //msliger + + for( Base = i = 0; i < MATCH_CODES; i++ ) + { + Lz.MatchCodeBase[ i ] = (unsigned) Base; + + for( k = 0; k < (1 << MatchCodeExtra[ i ]); k++ ) + { +#ifdef PAQ + Lz.MatchCodeTrans[ Base++ ] = i; +#else + Base++; +#endif + } + } + + for( Base = i = 0; i < WINDOW_CODES; i++ ) + { + if( Base < WindowSize ) + { + Count.WindowCode.Num = i + 1; + + if( Base < 1 << (TINY_WINDOW_MAX) ) + { + Count.TinyWindowCode.Num = i + 1; + } + + if( Base < 1L << (SHORT_WINDOW_MAX) ) + { + Count.ShortWindowCode.Num = i + 1; + } + } + + Lz.WindowCodeBase[ i ] = Base; + + Base += (1L << WindowCodeExtra[ i ]); + } + +#ifdef PAQ + for( i = 0; i < 2048; i++ ) + { + for( k = 0; Lz.WindowCodeBase[ k ] <= i; k++ ) + ; + + Lz.WindowCodeTrans[ i ] = k - 1; + } + + Base = 2; + + for( k = k - 1; k < WINDOW_CODES; k++ ) + { + for( i = 0; i < (1 << (WindowCodeExtra[ k ] - 10)); i++ ) + { + Lz.WindowCodeTrans[ 2048 + Base++ ] = k; + } + } +#endif + + Count.TokenType.Num = 7; + Count.TokenType.SortCountdown = 4; + + for( i = 0; i <= 7; i++ ) + { + Count.TokenType.Table[ i ].Freq = 7 - i; + Count.TokenType.Table[ i ].Symbol = i; + +#ifdef PAQ + Count.TokenType.Lookup[ i ] = i; +#endif + } + + Count.Literal1.Num = + Count.Literal2.Num = + Count.Literal3.Num = + Count.Literal4.Num = 64; + + Count.Literal1.SortCountdown = + Count.Literal2.SortCountdown = + Count.Literal3.SortCountdown = + Count.Literal4.SortCountdown = 4; + + for( i = 0; i <= 64; i++ ) + { + Count.Literal1.Table[ i ].Freq = 64 - i; + Count.Literal2.Table[ i ].Freq = 64 - i; + Count.Literal3.Table[ i ].Freq = 64 - i; + Count.Literal4.Table[ i ].Freq = 64 - i; + + Count.Literal1.Table[ i ].Symbol = i; + Count.Literal2.Table[ i ].Symbol = i; + Count.Literal3.Table[ i ].Symbol = i; + Count.Literal4.Table[ i ].Symbol = i; + +#ifdef PAQ + Count.Literal1.Lookup[ i ] = i; + Count.Literal2.Lookup[ i ] = i; + Count.Literal3.Lookup[ i ] = i; + Count.Literal4.Lookup[ i ] = i; +#endif + } + + Count.MatchCode.Num = MATCH_CODES; + Count.MatchCode.SortCountdown = 4; + + for( i = 0; i <= MATCH_CODES; i++ ) + { + Count.MatchCode.Table[ i ].Freq = MATCH_CODES - i; + Count.MatchCode.Table[ i ].Symbol = i; + +#ifdef PAQ + Count.MatchCode.Lookup[ i ] = i; +#endif + } + + Count.WindowCode.SortCountdown = 4; + Count.TinyWindowCode.SortCountdown = 4; + Count.ShortWindowCode.SortCountdown = 4; + + for( i = 0; i <= WINDOW_CODES; i++ ) + { + Count.WindowCode.Table[ i ].Freq = Count.WindowCode.Num - i; + Count.TinyWindowCode.Table[ i ].Freq = Count.TinyWindowCode.Num - i; + Count.ShortWindowCode.Table[ i ].Freq = Count.ShortWindowCode.Num - i; + + Count.WindowCode.Table[ i ].Symbol = i; + Count.TinyWindowCode.Table[ i ].Symbol = i; + Count.ShortWindowCode.Table[ i ].Symbol = i; + +#ifdef PAQ + Count.WindowCode.Lookup[ i ] = i; + Count.TinyWindowCode.Lookup[ i ] = i; + Count.ShortWindowCode.Lookup[ i ] = i; +#endif + } + +#ifndef UNPAQLIB //msliger + Arith_Init(); +#endif //msliger + } + + +void FAST Lz_Close( void ) + { + #ifdef DEBUG + #ifdef PAQ + printf( "\n\n" ); + printf( "Stats:\n" ); + printf( "Literal 0 = %ld\n", Lz.Stat[ 0 ] ); + printf( "Literal 1 = %ld\n", Lz.Stat[ 1 ] ); + printf( "Literal 2 = %ld\n", Lz.Stat[ 2 ] ); + printf( "Literal 3 = %ld\n", Lz.Stat[ 3 ] ); + printf( "Match 3 = %ld\n", Lz.Stat[ 4 ] ); + printf( "Match 4 = %ld\n", Lz.Stat[ 5 ] ); + printf( "Match 5+ = %ld\n", Lz.Stat[ 6 ] ); + #endif + #endif + +#ifndef PAQLIB //msliger +#ifndef UNPAQLIB //msliger + Arith_Close(); +#endif //msliger +#endif //msliger + } + + +void FAST Lz_Bump( COUNT_RECORD NEAR *Rec ) + { + COUNT NEAR *pTab; + int Num; + int i, j; + COUNT Scratch; + + pTab = Rec->Table; + Num = Rec->Num; + + if( --Rec->SortCountdown == 0 ) + { + Rec->SortCountdown = 50; + + // Change all the frequencies to their absolute values divided by two. + + for( i = 0; i < Num; i++ ) + { + pTab[ i ].Freq -= pTab[ i + 1 ].Freq; + + pTab[ i ].Freq++; + pTab[ i ].Freq >>= 1; + } + + // Sort them. + // Yes, this could be a faster sort but the list is never + // more than 64 items and it spends very little time here. + + for( i = 0; i < Num; i++ ) + { + for( j = i + 1; j < Num; j++ ) + { + if( pTab[ j ].Freq > pTab[ i ].Freq ) + { + Scratch = pTab[ i ]; + pTab[ i ] = pTab[ j ]; + pTab[ j ] = Scratch; + } + } + } + + // Recreate the relative frequencies. + + for( i = Num - 1; i >= 0; i-- ) + { + pTab[ i ].Freq += pTab[ i + 1 ].Freq; + } + +#ifdef PAQ + // Rebuild the lookup table. + + for( i = 0; i < Num; i++ ) + { + Rec->Lookup[ pTab[ i ].Symbol ] = i; + } +#endif + } + else /* SortCountDown != 0 */ + { + for( i = Num - 1; i >= 0; i-- ) + { + pTab[ i ].Freq >>= 1; + + if( pTab[ i ].Freq <= pTab[ i + 1 ].Freq ) + { + pTab[ i ].Freq = pTab[ i + 1 ].Freq + 1; + } + } + } + } + + +#ifdef PAQ + +// Maps a distance into a window code + +int FAST Lz_MapDistanceToWindowCode( long Distance ) + { + if( Distance < 2048 ) + { + return( Lz.WindowCodeTrans[ Distance ] ); + } + else + { + return( Lz.WindowCodeTrans[ 2048 + (Distance >> 10) ] ); + } + } + + +LONGDOUBLE FAST EstimateBits( long Occur, long Total ) + { + #ifdef DEBUG + if( Occur <= 0 || Total <= 0 ) + { + puts( "ERR: Domain" ); + exit( -1 ); + } + #endif + + return( MYLOG( (LONGDOUBLE)Total / (LONGDOUBLE)Occur ) ); + } + + +void FAST Lz_Encode_Symbol( COUNT_RECORD *Rec, int Sym ) + { + register COUNT NEAR *pTab; + SYMBOL Symbol; + int Index = Rec->Lookup[ Sym ]; + + Symbol.High = Rec->Table[ Index ].Freq; + Symbol.Low = Rec->Table[ Index + 1 ].Freq; + Symbol.Scale = Rec->Table[ 0 ].Freq; + + Arith_Encode_Symbol( &Symbol ); + + pTab = (COUNT NEAR *) Rec->Table; + + do + { + pTab->Freq += MAGIC_INC; + pTab++; + } + while( Index-- ); + + if( Rec->Table[ 0 ].Freq > MAGIC_MAX ) + { + Lz_Bump( Rec ); + } + } + + +LONGDOUBLE FAST Lz_Encode_Symbol_Cost( COUNT_RECORD *Rec, int Sym ) + { + int Index = Rec->Lookup[ Sym ]; + + return( EstimateBits( + Rec->Table[ Index ].Freq - Rec->Table[ Index + 1 ].Freq, + Rec->Table[ 0 ].Freq ) ); + } + + +LONGDOUBLE FAST Lz_Encode_Match_Cost( MATCH *Match ) + { + int Code; + LONGDOUBLE Total; + + switch( Match->Len ) + { + case 3: + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 4 ); + Code = Lz_MapDistanceToWindowCode( Match->Dist - 1 ); + Total += Lz_Encode_Symbol_Cost( &Count.TinyWindowCode, Code ); + break; + case 4: + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 5 ); + Code = Lz_MapDistanceToWindowCode( Match->Dist - 1 ); + Total += Lz_Encode_Symbol_Cost( &Count.ShortWindowCode, Code ); + break; + default: + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 6 ); + Code = Lz.MatchCodeTrans[ Match->Len - 5 ]; + Total += Lz_Encode_Symbol_Cost( &Count.MatchCode, Code ); + Total += MatchCodeExtra[ Code ]; + Code = Lz_MapDistanceToWindowCode( Match->Dist - 1 ); + Total += Lz_Encode_Symbol_Cost( &Count.WindowCode, Code ); + } + + Total += WindowCodeExtra[ Code ]; + + return( Total ); + } + + +void FAST Lz_Encode_Match( MATCH *Match ) + { + int Code; + + switch( Match->Len ) + { + case 3: + #ifdef DEBUG + Lz.Stat[ 4 ]++; + #endif + + Lz_Encode_Symbol( &Count.TokenType, 4 ); + Code = Lz_MapDistanceToWindowCode( Match->Dist - 1 ); + Lz_Encode_Symbol( &Count.TinyWindowCode, Code ); + break; + + case 4: + #ifdef DEBUG + Lz.Stat[ 5 ]++; + #endif + + Lz_Encode_Symbol( &Count.TokenType, 5 ); + Code = Lz_MapDistanceToWindowCode( Match->Dist - 1 ); + Lz_Encode_Symbol( &Count.ShortWindowCode, Code ); + break; + + default: + #ifdef DEBUG + Lz.Stat[ 6 ]++; + #endif + + Lz_Encode_Symbol( &Count.TokenType, 6 ); + Code = Lz.MatchCodeTrans[ Match->Len - 5 ]; + Lz_Encode_Symbol( &Count.MatchCode, Code ); + Arith_Encode_Bits( Match->Len - 5 - Lz.MatchCodeBase[ Code ], + MatchCodeExtra[ Code ] ); + Code = Lz_MapDistanceToWindowCode( Match->Dist - 1 ); + Lz_Encode_Symbol( &Count.WindowCode, Code ); + } + + Arith_Encode_Bits( Match->Dist - 1 - Lz.WindowCodeBase[ Code ], + WindowCodeExtra[ Code ] ); + } + + +LONGDOUBLE FAST Lz_Encode_Literal_Cost( int Chr ) + { + LONGDOUBLE Total; + + if( Chr < 64 ) + { + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 0 ) + + Lz_Encode_Symbol_Cost( &Count.Literal1, Chr ); + } + else + { + if( Chr < 128 ) + { + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 1 ) + + Lz_Encode_Symbol_Cost( &Count.Literal2, Chr - 64 ); + } + else + { + if( Chr < 192 ) + { + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 2 ) + + Lz_Encode_Symbol_Cost( &Count.Literal3, Chr - 128 ); + } + else + { + Total = Lz_Encode_Symbol_Cost( &Count.TokenType, 3 ) + + Lz_Encode_Symbol_Cost( &Count.Literal4, Chr - 192 ); + } + } + } + + return( Total ); + } + + +void FAST Lz_Encode_Literal( int Chr ) + { + if( Chr < 64 ) + { + #ifdef DEBUG + Lz.Stat[ 0 ]++; + #endif + Lz_Encode_Symbol( &Count.TokenType, 0 ); + Lz_Encode_Symbol( &Count.Literal1, Chr ); + } + else + { + if( Chr < 128 ) + { + #ifdef DEBUG + Lz.Stat[ 1 ]++; + #endif + Lz_Encode_Symbol( &Count.TokenType, 1 ); + Lz_Encode_Symbol( &Count.Literal2, Chr - 64 ); + } + else + { + if( Chr < 192 ) + { + #ifdef DEBUG + Lz.Stat[ 2 ]++; + #endif + Lz_Encode_Symbol( &Count.TokenType, 2 ); + Lz_Encode_Symbol( &Count.Literal3, Chr - 128 ); + } + else + { + #ifdef DEBUG + Lz.Stat[ 3 ]++; + #endif + Lz_Encode_Symbol( &Count.TokenType, 3 ); + Lz_Encode_Symbol( &Count.Literal4, Chr - 192 ); + } + } + } + } + + +#else //UNPAQ + +#define LZ_DECODE_SYMBOL(Rec,Sym) \ + { \ +/* SYMBOL Symbol; / I defined these at the top of */ \ +/* unsigned Counter; / the calling function because */ \ +/* int Index; / the compiler wasn't re-using */ \ +/* register COUNT NEAR *pTab; / the local variable space. */ \ + \ + Symbol.Scale = Rec.Table[ 0 ].Freq; \ + \ + Counter = Arith_GetCount( Symbol.Scale ); \ + \ + Index = 0; \ + \ + while ( Rec.Table[ Index + 1 ].Freq > Counter ) \ + { \ + Index++; \ + } \ + \ + Sym = Rec.Table[ Index ].Symbol; \ + \ + Symbol.High = Rec.Table[ Index ].Freq; \ + Symbol.Low = Rec.Table[ Index + 1 ].Freq; \ + \ + Arith_Remove_Symbol( Symbol ); \ + \ + pTab = (COUNT NEAR *) &Rec.Table; \ + \ + do \ + { \ + pTab->Freq += MAGIC_INC; \ + pTab++; \ + } \ + while( Index-- ); \ + \ + if( Rec.Table[ 0 ].Freq > MAGIC_MAX ) \ + { \ + Lz_Bump((COUNT_RECORD NEAR *)&Rec ); \ + } \ + } + + +void FAST Lz_NextToken( void ) + { + int Code; + MATCH Match; + SYMBOL Symbol; /* ref'd from macro */ + unsigned Counter; /* ref'd from macro */ + int Index; /* ref'd from macro */ + register COUNT NEAR *pTab; /* ref'd from macro */ + + LZ_DECODE_SYMBOL(Count.TokenType,Code); + + switch( Code ) + { + case 0: + LZ_DECODE_SYMBOL(Count.Literal1,Code); + DComp_Token_Literal( Code ); + break; + + case 1: + LZ_DECODE_SYMBOL(Count.Literal2,Code); + DComp_Token_Literal( Code + 64 ); + break; + + case 2: + LZ_DECODE_SYMBOL(Count.Literal3,Code); + DComp_Token_Literal( Code + 128 ); + break; + + case 3: + LZ_DECODE_SYMBOL(Count.Literal4,Code); + DComp_Token_Literal( Code + 192 ); + break; + + case 4: + Match.Len = 3; + LZ_DECODE_SYMBOL(Count.TinyWindowCode,Code); + Match.Dist = Lz.WindowCodeBase[ Code ] + + Arith_Decode_Bits( WindowCodeExtra[ Code ] ) + 1; + DComp_Token_Match( Match ); + break; + + case 5: + Match.Len = 4; + LZ_DECODE_SYMBOL(Count.ShortWindowCode,Code); + Match.Dist = Lz.WindowCodeBase[ Code ] + + Arith_Decode_Bits( WindowCodeExtra[ Code ] ) + 1; + DComp_Token_Match( Match ); + break; + + case 6: + LZ_DECODE_SYMBOL(Count.MatchCode,Code); + Match.Len = Lz.MatchCodeBase[ Code ] + + (short) Arith_Decode_Bits( MatchCodeExtra[ Code ] ) + 5; + LZ_DECODE_SYMBOL(Count.WindowCode,Code); + Match.Dist = Lz.WindowCodeBase[ Code ] + + Arith_Decode_Bits( WindowCodeExtra[ Code ] ) + 1; + DComp_Token_Match( Match ); + break; + } + } + +#endif + + +#ifdef PAQ + +// Estimates the number of bits required to represent a symbol +// given the total number of occurances of this symbol and the +// total number of occurances of all symbols (an order-0 probability). + +#ifndef __PCC__ + +#undef HUGE /* defined by MATH.H */ + +#include <math.h> + +//#if defined(_MIPS_) || defined(_ALPHA_) +#ifndef _X86_ +#ifndef logl +#define logl(x) ((LONGDOUBLE)log((double)(x))) +#endif +#ifndef M_LN2 +#define M_LN2 0.6931471805599 +#endif +#endif + +LONGDOUBLE FAST MYLOG( LONGDOUBLE x ) + { +//#if defined(_MIPS_) || defined(_ALPHA_) +#ifndef _X86_ + return( logl( x ) / M_LN2 ); +#else + + ASM fldln2 // st(0) = log( x ) + ASM fld x + ASM fyl2x + + ASM fldln2 // st(0) = st(0) / log( 2 ) + ASM fdivp st(1),st + + ASM fstp x + + return( x ); +#endif /* 0 */ + } +#endif /* ! PCC */ + +#endif /* PAQ */ diff --git a/private/windows/diamond/quantum/lz.h b/private/windows/diamond/quantum/lz.h new file mode 100644 index 000000000..a19994abf --- /dev/null +++ b/private/windows/diamond/quantum/lz.h @@ -0,0 +1,60 @@ +// LZ.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __LZ +#define __LZ + +#include "defs.h" + +#define MATCH_NUM 257 +#define MATCH_MIN 3 +#define MATCH_MAX (MATCH_NUM + MATCH_MIN - 1) +#define MATCH_CODES 27 + +#define WINDOW_MIN 10 // the sliding window size +#define WINDOW_DEFAULT 20 // 2**N bits +#define WINDOW_MAX 21 +#define WINDOW_CODES 42 +#define WINDOW_SHIFT 8 +#define WINDOW_BREAK (1 << WINDOW_SHIFT) +#define WINDOW_TOTAL ((1 << (WINDOW_MAX - WINDOW_SHIFT))+WINDOW_BREAK) + +// Matches of length MATCH_MIN use a shorter window because: +// +// 1. Encoding them with very large distances is inefficient. +// 2. They occur pretty often and deserve their own frequency table. + +#define TINY_WINDOW_MAX 12 // 4K max +#define SHORT_WINDOW_MAX 18 // 256K max + +#define MAGIC_MAX 3800 // was 4000 in 0.18 and earlier versions +#define MAGIC_INC 8 + +typedef struct + { + short Len; + long Dist; + } MATCH; + + +#ifdef STAFFORD +#define LONGDOUBLE long double +#else +#define LONGDOUBLE double +#endif + +void FAST Lz_Init( BYTE WindowBits ); //msliger +void FAST Lz_NextToken( void ); +void FAST Lz_Encode_Match( MATCH *Match ); +LONGDOUBLE FAST Lz_Encode_Match_Cost( MATCH *Match ); +void FAST Lz_Encode_Literal( int Ch ); +LONGDOUBLE FAST Lz_Encode_Literal_Cost( int Ch ); +void FAST Lz_Close( void ); + + +#endif diff --git a/private/windows/diamond/quantum/mem.h b/private/windows/diamond/quantum/mem.h new file mode 100644 index 000000000..9a476ba0e --- /dev/null +++ b/private/windows/diamond/quantum/mem.h @@ -0,0 +1,11 @@ +// MEM.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + + +void *Mem_Malloc( long x ); +void Mem_Free( void *x ); diff --git a/private/windows/diamond/quantum/qci.c b/private/windows/diamond/quantum/qci.c new file mode 100644 index 000000000..50b00d0bf --- /dev/null +++ b/private/windows/diamond/quantum/qci.c @@ -0,0 +1,249 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * QCI.C: Quantum Compression Interface + * + * History: + * 20-Jan-1994 msliger Initial version (stub). + * 23-Jan-1994 msliger Real version (not a stub). + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * normalized MCI_MEMORY type. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * Changed MCI_MEMORY to MI_MEMORY. + * Restructured allocation/destruction. + * 15-Mar-1994 msliger Changes for 32 bits. + * 22-Mar-1994 msliger Changed interface USHORT to UINT. + * 26-May-1994 msliger Adapted to Quantum compression from MCI.C + * 27-May-1994 msliger Added configuration parameter. + * 30-May-1994 msliger Changed to QCI* names. + * 15-Jun-1994 msliger Use passed in alloc/free everywhere + */ + +/* --- preprocessor ------------------------------------------------------- */ + +#include <stdio.h> /* for NULL */ +#include <string.h> /* for memcpy() */ + +#include "qci.h" /* types, prototype verification, error codes */ + +#include "comp.h" /* features in COMP.C */ + +#include "rtl.h" /* Rtl_Malloc/Free definitions */ + +#define MAX_GROWTH 10240 /* don't know what it will really be */ + + +/* MAKE_SIGNATURE - Construct a structure signature + * + * Entry: + * a,b,c,d - four characters + * + * Exit: + * Returns constructed SIGNATURE + * + * Example: + * strct->signature = MAKE_SIGNATURE('b','e','n','s') + */ + +#define MAKE_SIGNATURE(a,b,c,d) (a + (b<<8) + (c<<16) + (d<<24)) +#define BAD_SIGNATURE (0L) +#define QCI_SIGNATURE MAKE_SIGNATURE('Q','C','I','C') + +/* --- QCI context structure ---------------------------------------------- */ + +typedef ULONG SIGNATURE; /* structure signature */ + +struct QCI_CONTEXT /* private structure */ +{ + SIGNATURE signature; /* for validation */ + PFNALLOC pfnAlloc; /* memory alloc function */ + PFNFREE pfnFree; /* memory free function */ + UINT cbDataBlockMax; /* promised max data size */ +}; + +typedef struct QCI_CONTEXT FAR *PMCC_CONTEXT; /* a pointer to one */ + +#define PMCCfromHMC(h) ((PMCC_CONTEXT)(h)) /* handle to pointer */ +#define HMCfromPMCC(p) ((QCI_CONTEXT_HANDLE)(p)) /* pointer to handle */ + + +/* --- local variables ---------------------------------------------------- */ + +/* + * pmccLast - Remember last QCI context for use by Rtl_Malloc + * and Rtl_Free. + */ +static PMCC_CONTEXT pmccLast; + + +/* --- QCICreateCompression() --------------------------------------------- */ + +int DIAMONDAPI QCICreateCompression( + UINT * pcbDataBlockMax, /* max uncompressed data block */ + void FAR * pvConfiguration, /* implementation-defined */ + PFNALLOC pfnma, /* Memory allocation function */ + PFNFREE pfnmf, /* Memory free function */ + UINT * pcbDstBufferMin, /* gets required output buffer */ + QCI_CONTEXT_HANDLE * pmchHandle) /* gets newly-created handle */ +{ + PMCC_CONTEXT context; /* new context */ + int FAR *pConfiguration; /* to get configuration details */ + BYTE cWindowBits; /* for Quantum implementation */ + int iCompressionLevel; /* for Quantum implementation */ + + *pmchHandle = (QCI_CONTEXT_HANDLE) 0; /* wait until it's valid */ + + pConfiguration = pvConfiguration; /* get a pointer we can use */ + cWindowBits = (BYTE) pConfiguration[0]; /* get window bits config */ + iCompressionLevel = pConfiguration[1]; /* get compress level config */ + + if ((cWindowBits < 10) || (cWindowBits > 21)) + { + return(MCI_ERROR_CONFIGURATION); /* can't accept that */ + } + + if ((iCompressionLevel < 1) || (iCompressionLevel > 7)) + { + return(MCI_ERROR_CONFIGURATION); /* can't accept that */ + } + + if (pConfiguration[2] != -1) + { + return(MCI_ERROR_CONFIGURATION); /* don't know any others */ + } + + if ((*pcbDataBlockMax == 0) || (*pcbDataBlockMax > 32768u)) + { + *pcbDataBlockMax = 32768u; /* help with source block size */ + } + + context = pfnma(sizeof(struct QCI_CONTEXT)); + if (context == NULL) + { + return(MCI_ERROR_NOT_ENOUGH_MEMORY); /* if can't allocate */ + } + + context->pfnAlloc = pfnma; + context->pfnFree = pfnmf; + context->cbDataBlockMax = *pcbDataBlockMax; /* remember agreement */ + context->signature = QCI_SIGNATURE; + pmccLast = context; /* Set for Rtl_Malloc/Free */ + + *pcbDstBufferMin = /* we'll expand sometimes */ + *pcbDataBlockMax + MAX_GROWTH; + + Comp_Init(cWindowBits,iCompressionLevel); /* setup compressor */ + + /* pass context back to caller */ + + *pmchHandle = HMCfromPMCC(context); + + return(MCI_ERROR_NO_ERROR); /* tell caller all is well */ +} + +/* --- QCICompress() ------------------------------------------------------ */ + +int DIAMONDAPI QCICompress( + QCI_CONTEXT_HANDLE hmc, /* compression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source actual size */ + void FAR * pbDst, /* target buffer */ + UINT cbDst, /* size of target buffer */ + UINT * pcbResult) /* gets target actual size */ +{ + PMCC_CONTEXT context; /* pointer to the context */ + int result; /* return code */ + + context = PMCCfromHMC(hmc); /* get pointer from handle */ + pmccLast = context; /* Save for Rtl_Malloc/Free */ + + if (context->signature != QCI_SIGNATURE) + { + return(MCI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + if (cbSrc > context->cbDataBlockMax) + { + return(MCI_ERROR_BAD_PARAMETERS); /* violated max block promise */ + } + + if (cbDst < (context->cbDataBlockMax + MAX_GROWTH)) + { + return(MCI_ERROR_BAD_PARAMETERS); /* violated min buffer request */ + } + + result = Comp_CompressBlock(pbSrc,cbSrc,pbDst,cbDst,pcbResult); + + if (result == 0) + { + return(MCI_ERROR_NO_ERROR); /* report no failure */ + } + else + { + return(MCI_ERROR_FAILED); /* report failure */ + } +} + +/* --- QCIResetCompression() ---------------------------------------------- */ + +int DIAMONDAPI QCIResetCompression(QCI_CONTEXT_HANDLE hmc) +{ + PMCC_CONTEXT context; /* pointer to the context */ + + context = PMCCfromHMC(hmc); /* get pointer from handle */ + pmccLast = context; /* Save for Rtl_Malloc/Free */ + + if (context->signature != QCI_SIGNATURE) + { + return(MCI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + Comp_Reset(); /* do it */ + + return(MCI_ERROR_NO_ERROR); /* if tag is OK */ +} + +/* --- QCIDestroyCompression() -------------------------------------------- */ + +int DIAMONDAPI QCIDestroyCompression(QCI_CONTEXT_HANDLE hmc) +{ + PMCC_CONTEXT context; /* pointer to context */ + + context = PMCCfromHMC(hmc); /* get pointer from handle */ + pmccLast = context; /* Save for Rtl_Malloc/Free */ + + if (context->signature != QCI_SIGNATURE) + { + return(MCI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + Comp_Close(); /* shut down compressor */ + + context->signature = BAD_SIGNATURE; /* destroy signature */ + + context->pfnFree(context); /* self-destruct */ + pmccLast = NULL; /* No context for Rtl_Malloc/Free */ + + return(MCI_ERROR_NO_ERROR); /* success */ +} + +/* --- Rtl_Malloc() ------------------------------------------------------- */ + +void * FAST Rtl_Malloc( long x ) + { + return pmccLast->pfnAlloc((ULONG) x); + } + + +/* --- Rtl_Free() --------------------------------------------------------- */ + +void FAST Rtl_Free( void *x ) + { + pmccLast->pfnFree(x); + } + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/quantum/qci.h b/private/windows/diamond/quantum/qci.h new file mode 100644 index 000000000..27eeda50b --- /dev/null +++ b/private/windows/diamond/quantum/qci.h @@ -0,0 +1,268 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993,1994 + * All Rights Reserved. + * + * QCI.H - Diamond Memory Compression Interface (QCI) + * + * History: + * 30-May-1994 msliger Created from MCI.H by global replace. + * 31-May-1994 msliger Documented the configuration info. + * 08-Aug-1994 msliger pragma pack'd configuration array. + * + * Functions: + * QCICreateCompression - Create and reset an QCI compression context + * QCICloneCompression - Make a copy of a compression context + * QCICompress - Compress a block of data + * QCIResetCompression - Reset compression context + * QCIDestroyCompression - Destroy QCI compression context + * + * Types: + * QCI_CONTEXT_HANDLE - Handle to an QCI compression context + * PFNALLOC - Memory allocation function for QCI + * PFNFREE - Free memory function for QCI + */ + +/* --- types -------------------------------------------------------------- */ + +#ifndef DIAMONDAPI +#define DIAMONDAPI __cdecl +#endif + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif + +#ifndef _UINT_DEFINED +#define _UINT_DEFINED +typedef unsigned int UINT; +#endif + +#ifndef _ULONG_DEFINED +#define _ULONG_DEFINED +typedef unsigned long ULONG; +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE huge +#else +#define HUGE +#endif +#endif + +#ifndef _MI_MEMORY_DEFINED +#define _MI_MEMORY_DEFINED +typedef void HUGE * MI_MEMORY; +#endif + +#ifndef _MHANDLE_DEFINED +#define _MHANDLE_DEFINED +typedef unsigned long MHANDLE; +#endif + +/* --- QCI-defined types -------------------------------------------------- */ + +/* QCI_CONTEXT_HANDLE - Handle to an QCI compression context */ + +typedef MHANDLE QCI_CONTEXT_HANDLE; /* hmc */ + + +/*** PFNALLOC - Memory allocation function for QCI + * + * Entry: + * cb - Size in bytes of memory block to allocate + * + * Exit-Success: + * Returns !NULL pointer to memory block + * + * Exit-Failure: + * Returns NULL; insufficient memory + */ +#ifndef _PFNALLOC_DEFINED +#define _PFNALLOC_DEFINED +typedef MI_MEMORY (FAR DIAMONDAPI *PFNALLOC)(ULONG cb); /* pfnma */ +#endif + + +/*** PFNFREE - Free memory function for QCI + * + * Entry: + * pv - Memory block allocated by matching PFNALLOC function + * + * Exit: + * Memory block freed. + */ +#ifndef _PFNFREE_DEFINED +#define _PFNFREE_DEFINED +typedef void (FAR DIAMONDAPI *PFNFREE)(MI_MEMORY pv); /* pfnmf */ +#endif + +/* --- prototypes --------------------------------------------------------- */ + +/*** QCICreateCompression - Create QCI compression context + * + * Entry: + * pcbDataBlockMax *largest uncompressed data block size desired, + * gets largest uncompressed data block allowed + * pvConfiguration passes implementation-specific info to compressor. + * pfnma memory allocation function pointer + * pfnmf memory free function pointer + * pcbDstBufferMin gets required compressed data buffer size + * pmchHandle gets newly-created context's handle + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * *pcbDataBlockMax, *pcbDstBufferMin, *pmchHandle filled in. + * + * Exit-Failure: + * MCI_ERROR_NOT_ENOUGH_MEMORY, could not allocate enough memory. + * MCI_ERROR_BAD_PARAMETERS, something wrong with parameters. + */ +int FAR DIAMONDAPI QCICreateCompression( + UINT FAR * pcbDataBlockMax, /* max uncompressed data block size */ + void FAR * pvConfiguration, /* See QUANTUMCONFIGURATION */ + PFNALLOC pfnma, /* Memory allocation function ptr */ + PFNFREE pfnmf, /* Memory free function ptr */ + UINT FAR * pcbDstBufferMin, /* gets required output buffer size */ + QCI_CONTEXT_HANDLE FAR *pmchHandle); /* gets newly-created handle */ + + +/*** QCICloneCompression - Make a copy of a compression context + * + * Entry: + * hmc handle to current compression context + * pmchHandle gets newly-created handle + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * *pmchHandle filled in. + * + * Exit-Failure: + * Returns: + * MCI_ERROR_BAD_PARAMETERS, something wrong with parameters. + * MCI_ERROR_NOT_ENOUGH_MEMORY, could not allocate enough memory. + * + * NOTES: + * (1) This API is intended to permit "roll-back" of a sequence of + * of QCICompress() calls. Before starting a sequence that may need + * to be rolled-back, use QCICloneCompression() to save the state of + * the compression context, then do the QCICompress() calls. If the + * sequence is successful, the "cloned" hmc can be destroyed with + * QCIDestroyCompression(). If the sequence is *not* successful, then + * the original hmc can be destroyed, and the cloned one can be used + * to restart as if the sequence of QCICompress() calls had never + * occurred. + */ +int FAR DIAMONDAPI QCICloneCompression( + QCI_CONTEXT_HANDLE hmc, /* current compression context */ + QCI_CONTEXT_HANDLE *pmchHandle); /* gets newly-created handle */ + + +/*** QCICompress - Compress a block of data + * + * Entry: + * hmc handle to compression context + * pbSrc source buffer (uncompressed data) + * cbSrc size of data to be compressed + * pbDst destination buffer (for compressed data) + * cbDst size of destination buffer + * pcbResult receives compressed size of data + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * *pcbResult has size of compressed data in pbDst. + * Compression context possibly updated. + * + * Exit-Failure: + * MCI_ERROR_BAD_PARAMETERS, something wrong with parameters. + */ +int FAR DIAMONDAPI QCICompress( + QCI_CONTEXT_HANDLE hmc, /* compression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source buffer size */ + void FAR * pbDst, /* target buffer */ + UINT cbDst, /* target buffer size */ + UINT FAR * pcbResult); /* gets target data size */ + + +/*** QCIResetCompression - Reset compression history (if any) + * + * De-compression can only be started on a block which was compressed + * immediately following a QCICreateCompression() or QCIResetCompression() + * call. This function forces such a new "compression boundary" to be + * created (only by causing the compressor to ignore history, can the data + * output be decompressed without history.) + * + * Entry: + * hmc - handle to compression context + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * Compression context reset. + * + * Exit-Failure: + * Returns MCI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI QCIResetCompression(QCI_CONTEXT_HANDLE hmc); + + +/*** QCIDestroyCompression - Destroy QCI compression context + * + * Entry: + * hmc - handle to compression context + * + * Exit-Success: + * Returns MCI_ERROR_NO_ERROR; + * Compression context destroyed. + * + * Exit-Failure: + * Returns MCI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI QCIDestroyCompression(QCI_CONTEXT_HANDLE hmc); + +/* --- constants ---------------------------------------------------------- */ + +/* return codes */ + +#define MCI_ERROR_NO_ERROR 0 +#define MCI_ERROR_NOT_ENOUGH_MEMORY 1 +#define MCI_ERROR_BAD_PARAMETERS 2 +#define MCI_ERROR_BUFFER_OVERFLOW 3 +#define MCI_ERROR_FAILED 4 +#define MCI_ERROR_CONFIGURATION 5 + +/* --- Quantum configuration details ------------------------------------- */ + +/*** Quantum pvConfiguration structure + * + * For the Quantum compressor, two parameters are configurable, the + * "compression level", which controls the compressor's aggression, + * and "window bits", which defines the size of the buffer needed by + * the decompressor (which affects the compressor's output). + */ + +#pragma pack (2) + +typedef struct { + int WindowBits; // log2(buffersize), 10..21 (1K..2M) + int CompressionLevel; // 1..3 = fast/less compression + // 4..7 = slow/more compression +//BUGBUG 01-Jun-1994 bens What is this 3rd int for? + int HackHack; // Has to be -1, why? +} QUANTUMCONFIGURATION; /* qcfg */ + +#pragma pack () + +typedef QUANTUMCONFIGURATION *PQUANTUMCONFIGURATION; /* pqcfg */ + +/* ----------------------------------------------------------------------- */ diff --git a/private/windows/diamond/quantum/qci.nt/makefile b/private/windows/diamond/quantum/qci.nt/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/quantum/qci.nt/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/quantum/qci.nt/sources b/private/windows/diamond/quantum/qci.nt/sources new file mode 100644 index 000000000..b9c2209b1 --- /dev/null +++ b/private/windows/diamond/quantum/qci.nt/sources @@ -0,0 +1,16 @@ +MAJORCOMP=diamond +MINORCOMP=quantum_qci + +TARGETNAME=qci +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +C_DEFINES=-DBIT_CONSTANTS -DPAQ -DPAQLIB + +SOURCES=..\qci.c \ + ..\comp.c \ + ..\arith.c \ + ..\lz.c \ + ..\bit.c + +UMTYPE=console diff --git a/private/windows/diamond/quantum/qdi.c b/private/windows/diamond/quantum/qdi.c new file mode 100644 index 000000000..b9e27bbcd --- /dev/null +++ b/private/windows/diamond/quantum/qdi.c @@ -0,0 +1,446 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * QDI.C: Quantum Decompression Interface + * + * History: + * 20-Jan-1994 msliger Initial version. + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * 22-Mar-1994 msliger Changed interface USHORT to UINT. + * 28-May-1994 msliger Created from MDI.C for Quantum. + * Added configuration parameter. + * 30-May-1994 msliger Changed to QDI* names. + * 08-Jul-1994 msliger Support for disk-based ring buffer. + * 27-Jul-1994 msliger Changed to allow ASM decompressor. + * 10-Aug-1994 msliger Changed to assert the requested decompress + * size for improved resistance to corrupted + * input data. + * 18-Aug-1994 msliger Implemented 286 decompress option. + * 31-Jan-1995 msliger Supported QDICreateDecompression query. + * 01-Feb-1995 msliger QDICreate no longer reserves memory. + */ + +/* --- preprocessor ------------------------------------------------------- */ + +/* + * Quantum Decompression is a very complicated, time consuming process, so + * we wrote a hand tuned 386 version. Unfortunately, some environments + * (80286 Win3.x machines and the Windows NT x86 emulation for Mips, Alpha, + * etc.) cannot run 386 code. So, we need to build different flavors of + * QDI.LIB: + * 16-bit QDI.LIB - has 286 & 386 versions + * 16-bit QDI3.LIB - 386-only version + * 32-bit QDI.LIB - 32-bit only version (for Win32 apps) + * + * There are two symbols which can be defined to affect how this code is + * built, USE_C_SRC and USE_386_ASM: + * + * USE_386_ASM Links the 386 assembler source (for i386+ non-Win32 only!) + * + * USE_C_SRC Links the C source. For any application. For + * 16-bit applications, this forces use of large memory model, + * and will generate externs for certain compiler run-time + * helpers, like __aFulmul. + * This is the DEFAULT if neither symbol is defined. + * + * These symbols can be defined in the following combinations: + * USE_386_ASM Only the 386 ASM code for 16-bit environments is available. + * ==> This is intended for Chicago setup, since Chicago only + * runs on i386 or better CPUs. + * + * USE_C_SRC Only the "portable" C code is available. + * ==> For Win32 builds, used by NT setup and Win32 EXTRACT.EXE, + * and Win32 ACME Setup. + * + * <both> Both "portable" C and the 16-bit 386 are available. The + * caller can force the use of the C code (compiled for 286, + * for compatibility purposes) or the 386 code, *or* can allow + * QDI to detect the CPU type and select the appropriate code. + * For 16-bit applications, this forces use of large memory + * model, and will generate externs for certain compiler run- + * time helpers, like __aNulshr. + * ==> For 16-bit ACME, which has to pass down the CPU type as + * determined by calling the Win16 API, since our CPU + * detection code is not reliable in 286-prot mode! + * ==> For 16-bit EXTRACT.EXE, which will rely upon the CPU + * detection code! + * + * <neither> Same as USE_C_SRC + */ + +#include <stdio.h> /* for NULL */ + +#ifdef USE_386_ASM +#ifdef USE_C_SRC +#define USE_BOTH /* means both are present */ +#endif +#else /* if not USE_386_ASM */ +#ifndef USE_C_SRC +#define USE_C_SRC /* make sure USE_C_SRC is defined if not USE_386_ASM */ +#endif +#endif + +#include "qdi.h" /* types, prototype verification, error codes */ +#include "qdi_int.h" /* QDI internal structures */ + +#ifdef USE_C_SRC +#include "dcomp.h" /* features in DCOMP.C */ +#include "rtl.h" /* functions we gotta do for you */ +#endif + +#ifdef USE_386_ASM +#include "decomp.h" /* features in DECOMP.ASM */ +#endif + +#ifdef USE_BOTH +static int GetCPUtype(void); /* internal CPU determination */ +#endif + +#define MAX_GROWTH 10240 /* don't know what it will really be */ + +#pragma warning(disable:4704) /* because of in-line asm code */ + +/* MAKE_SIGNATURE - Construct a structure signature + * + * Entry: + * a,b,c,d - four characters + * + * Exit: + * Returns constructed SIGNATURE + * + * Example: + * strct->signature = MAKE_SIGNATURE('b','e','n','s') + */ + +#define MAKE_SIGNATURE(a,b,c,d) (a + (b<<8) + (c<<16) + (d<<24)) +#define BAD_SIGNATURE (0L) +#define QDI_SIGNATURE MAKE_SIGNATURE('Q','D','I','C') + +/* --- QDI context structure ---------------------------------------------- */ + +PMDC_CONTEXT lastContext; /* needed for callbacks */ + +#define PMDCfromHMD(h) ((PMDC_CONTEXT)(h)) /* handle to pointer */ +#define HMDfromPMDC(p) ((QDI_CONTEXT_HANDLE)(p)) /* pointer to handle */ + +/* --- QDICreateDecompression() ------------------------------------------- */ + +int FAR DIAMONDAPI QDICreateDecompression( + UINT FAR * pcbDataBlockMax, /* max uncompressed data block */ + void FAR * pvConfiguration, /* implementation-defined */ + PFNALLOC pfnma, /* Memory allocation function */ + PFNFREE pfnmf, /* Memory free function */ + UINT FAR * pcbSrcBufferMin, /* gets required input buffer */ + QDI_CONTEXT_HANDLE FAR * pmdhHandle, /* gets newly-created handle */ + PFNOPEN pfnopen, /* open a file callback */ + PFNREAD pfnread, /* read a file callback */ + PFNWRITE pfnwrite, /* write a file callback */ + PFNCLOSE pfnclose, /* close a file callback */ + PFNSEEK pfnseek) /* seek in file callback */ +{ + PMDC_CONTEXT context; /* new context */ + PFQUANTUMDECOMPRESS pConfig; /* to get configuration details */ + + pConfig = pvConfiguration; /* get a pointer we can use */ + + if ((pConfig->WindowBits < 10) || (pConfig->WindowBits > 21)) + { + return(MDI_ERROR_CONFIGURATION); /* can't accept that */ + } + + if ((*pcbDataBlockMax == 0) || (*pcbDataBlockMax > 32768u)) + { + *pcbDataBlockMax = 32768u; /* help with source block size */ + } + + *pcbSrcBufferMin = /* we'll expand sometimes */ + *pcbDataBlockMax + MAX_GROWTH; + + if (pmdhHandle == NULL) /* if no context requested, */ + { + return(MDI_ERROR_NO_ERROR); /* return from query mode */ + } + +#ifdef USE_BOTH + if (pConfig->fCPUtype == QDI_CPU_UNKNOWN) + { + pConfig->fCPUtype = GetCPUtype(); /* figure out if not told */ + } + else if ((pConfig->fCPUtype != QDI_CPU_80286) && + (pConfig->fCPUtype != QDI_CPU_80386)) + { + return(MDI_ERROR_CONFIGURATION); /* bogus parameter used */ + } +#else /* not USE_BOTH */ +#ifdef USE_386_ASM + if (pConfig->fCPUtype == QDI_CPU_UNKNOWN) + { + pConfig->fCPUtype = QDI_CPU_80386; + } + else if (pConfig->fCPUtype != QDI_CPU_80386) + { + return(MDI_ERROR_CONFIGURATION); /* bogus parameter used */ + } +#else /* must be USE_C_SRC only */ + /** Nothing to do -- we just call the C code, which was either compiled */ + /* 16-bit or 32-bit. We could validate the parms, but no real need to */ +#endif /* USE_386_ASM */ +#endif /* USE_BOTH */ + + *pmdhHandle = (QDI_CONTEXT_HANDLE) 0; /* wait until it's valid */ + + context = pfnma(sizeof(struct QDI_CONTEXT)); + if (context == NULL) + { + return(MDI_ERROR_NOT_ENOUGH_MEMORY); /* if can't allocate */ + } + + context->pfnAlloc = pfnma; /* remember where alloc() is */ + context->pfnFree = pfnmf; /* remember where free() is */ + context->pfnOpen = pfnopen; /* remember where pfnopen() is */ + context->pfnRead = pfnread; /* remember where pfnread() is */ + context->pfnWrite = pfnwrite; /* remember where pfnwrite() is */ + context->pfnClose = pfnclose; /* remember where pfnclose() is */ + context->pfnSeek = pfnseek; /* remember where pfnseek() is */ + context->cbDataBlockMax = *pcbDataBlockMax; /* remember agreement */ + context->fCPUtype = pConfig->fCPUtype; /* remember CPU type */ + + context->signature = QDI_SIGNATURE; /* install signature */ + + lastContext = context; /* hand off to memory wrapper */ + +#ifdef USE_C_SRC +#ifdef USE_BOTH + if (context->fCPUtype == QDI_CPU_80286) +#endif /* USE_BOTH */ + { + /** Do the C source init **/ + if (DComp_Init((BYTE) (pConfig->WindowBits)) != 0) /* setup decompressor */ + { + pfnmf(context); /* self-destruct */ + + return(MDI_ERROR_NOT_ENOUGH_MEMORY); + } + } +#endif /* USE_C_SRC */ + +#ifdef USE_386_ASM +#ifdef USE_BOTH + else /* if (context->fCPUtype == QDI_CPU_80386) */ +#endif /* USE_BOTH */ + { + /** Do the 386 ASM init **/ + if (DComp386_Init((BYTE) (pConfig->WindowBits)) != 0) /* setup decompressor */ + { + pfnmf(context); /* self-destruct */ + + return(MDI_ERROR_NOT_ENOUGH_MEMORY); + } + } +#endif /* USE_386_ASM */ + + /* pass context back to caller */ + + *pmdhHandle = HMDfromPMDC(context); + + return(MDI_ERROR_NO_ERROR); /* tell caller all is well */ +} + +/* --- QDIDecompress() ---------------------------------------------------- */ + +int FAR DIAMONDAPI QDIDecompress( + QDI_CONTEXT_HANDLE hmd, /* decompression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source actual size */ + void FAR * pbDst, /* target buffer */ + UINT FAR * pcbResult) /* gets actual target size */ +{ + PMDC_CONTEXT context; /* pointer to the context */ + int result; /* return code */ + + context = PMDCfromHMD(hmd); /* get pointer from handle */ + + if (context->signature != QDI_SIGNATURE) + { + return(MDI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + lastContext = context; /* hand off to memory wrapper */ + + if (*pcbResult > context->cbDataBlockMax) + { + return(MDI_ERROR_BUFFER_OVERFLOW); /* violated max block promise */ + } + +#ifdef USE_C_SRC +#ifdef USE_BOTH + if (context->fCPUtype == QDI_CPU_80286) +#endif /* USE_BOTH */ + { + result = DComp_DecompressBlock(pbSrc,cbSrc,pbDst,*pcbResult); + } +#endif /* USE_C_SRC */ + +#ifdef USE_386_ASM +#ifdef USE_BOTH + else /* (context->fCPUtype == QDI_CPU_80386) */ +#endif /* USE_BOTH */ + { + result = DComp386_DecompressBlock(pbSrc,cbSrc,pbDst,*pcbResult); + } +#endif /* USE_386_ASM */ + + if (result == 0) + { + return(MDI_ERROR_NO_ERROR); /* report no failure */ + } + else + { + return(MDI_ERROR_FAILED); /* report failure */ + } +} + +/* --- QDIResetDecompression() -------------------------------------------- */ + +int FAR DIAMONDAPI QDIResetDecompression(QDI_CONTEXT_HANDLE hmd) +{ + PMDC_CONTEXT context; /* pointer to the context */ + + context = PMDCfromHMD(hmd); /* get pointer from handle */ + + if (context->signature != QDI_SIGNATURE) + { + return(MDI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + lastContext = context; /* hand off to memory wrapper */ + +#ifdef USE_C_SRC +#ifdef USE_BOTH + if (context->fCPUtype == QDI_CPU_80286) +#endif /* USE_BOTH */ + { + DComp_Reset(); /* do it */ + } +#endif /* USE_C_SRC */ + +#ifdef USE_386_ASM +#ifdef USE_BOTH + else /* if (context->fCPUtype == QDI_CPU_80386) */ +#endif /* USE_BOTH */ + { + DComp386_Reset(); /* do it */ + } +#endif /* USE_386_ASM */ + + return(MDI_ERROR_NO_ERROR); /* if tag is OK */ +} + +/* --- QDIDestroyDecompression() ------------------------------------------ */ + +int FAR DIAMONDAPI QDIDestroyDecompression(QDI_CONTEXT_HANDLE hmd) +{ + PMDC_CONTEXT context; /* pointer to the context */ + + context = PMDCfromHMD(hmd); /* get pointer from handle */ + + if (context->signature != QDI_SIGNATURE) + { + return(MDI_ERROR_BAD_PARAMETERS); /* missing signature */ + } + + lastContext = context; /* hand off to memory wrapper */ + +#ifdef USE_C_SRC +#ifdef USE_BOTH + if (context->fCPUtype == QDI_CPU_80286) +#endif /* USE_BOTH */ + { + DComp_Close(); /* shut down decompressor */ + } +#endif /* USE_C_SRC */ + +#ifdef USE_386_ASM +#ifdef USE_BOTH + else /* if (context->fCPUtype == QDI_CPU_80386) */ +#endif /* USE_BOTH */ + { + DComp386_Close(); /* shut down decompressor */ + } +#endif /* USE_386_ASM */ + + context->signature = BAD_SIGNATURE; /* destroy signature */ + + context->pfnFree(context); /* self-destruct */ + + return(MDI_ERROR_NO_ERROR); /* success */ +} + + +#ifdef USE_C_SRC + +/* --- Rtl_Malloc() ------------------------------------------------------- */ + +void * FAST Rtl_Malloc( long x ) + { + return(lastContext->pfnAlloc((ULONG) x)); + } + +/* --- Rtl_Free() --------------------------------------------------------- */ + +void FAST Rtl_Free( void *x ) + { + lastContext->pfnFree(x); + } + +#endif /* USE_C_SRC */ + +#ifdef USE_BOTH + +/* --- GetCPUtype() ------------------------------------------------------- */ + +#define ASM __asm + +/*** GetCPUtype - Determine CPU type (286 vs. 386 or better) + * + * [Logic taken from emm386/smartdrv.] + * + * Entry: + * none: + * + * Exit: + * Returns QDI_CPU_80286 if on i286 or compatible. + * Returns QDI_CPU_80386 if on i386 or better (or compatible). + */ +static int GetCPUtype(void) +{ + int f286; + + ASM pushf /* preserve original flags */ + ASM mov ax,7000h /* try to set some special flags bits */ + ASM push ax /* put up for popf */ + ASM popf /* pull number into flags */ + ASM pushf /* push flags back */ + ASM pop ax /* pull flags into AX */ + ASM popf /* restore original flags */ + ASM and ax,7000h /* check for 386-specific bits */ + ASM mov f286,ax /* return the result */ + + if (f286 != 0) + { + return(QDI_CPU_80386); + } + else + { + return(QDI_CPU_80286); + } +} + +#endif /* USE_BOTH */ + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/quantum/qdi.h b/private/windows/diamond/quantum/qdi.h new file mode 100644 index 000000000..e3a635290 --- /dev/null +++ b/private/windows/diamond/quantum/qdi.h @@ -0,0 +1,445 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993,1994 + * All Rights Reserved. + * + * QDI.H - Diamond Memory Decompression Interface (QDI) + * + * History: + * 01-Dec-1993 bens Initial version. + * 16-Jan-1994 msliger Split into MCI, MDI. + * 11-Feb-1994 msliger Changed M*ICreate() to adjust size. + * 13-Feb-1994 msliger revised type names, ie, UINT16 -> UINT. + * changed handles to HANDLEs. + * normalized MDI_MEMORY type. + * 24-Feb-1994 msliger Changed alloc,free to common typedefs. + * Changed HANDLE to MHANDLE. + * Changed MDI_MEMORY to MI_MEMORY. + * 22-Mar-1994 msliger Changed !INT32 to BIT16. + * Changed interface USHORT to UINT. + * 28-May-1994 msliger Added configuration parms, error code. + * 30-May-1994 msliger Created from MDI.H by global replace. + * 31-May-1994 msliger Documented the configuration info. + * 01-Jul-1994 msliger Added support for ring buffer on disk. + * 27-Jul-1994 msliger Added more def's to support ASM build. + * 08-Aug-1994 msliger pragma pack'd configuration array. + * 18-Aug-1994 msliger Added CPU type to configuration array. + * 22-Aug-1994 msliger Clarified *pcbResult in QDIDecompress(), + * changed it's name to pcbDecompressed. + * 31-Jan-1995 msliger Supported MDICreateDecompression query. + * + * Functions: + * QDICreateDecompression - Create and reset an QDI decompression context + * QDIDecompress - Decompress a block of data + * QDIResetDecompression - Reset QDI decompression context + * QDIDestroyDecompression - Destroy QDI Decompression context + * + * Types: + * QDI_CONTEXT_HANDLE - Handle to an QDI decompression context + * PFNALLOC - Memory allocation function for QDI + * PFNFREE - Free memory function for QDI + */ + +/* --- types -------------------------------------------------------------- */ + +#ifndef DIAMONDAPI +#define DIAMONDAPI __cdecl +#endif + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif + +#ifndef _UINT_DEFINED +#define _UINT_DEFINED +typedef unsigned int UINT; +#endif + +#ifndef _ULONG_DEFINED +#define _ULONG_DEFINED +typedef unsigned long ULONG; +#endif + +#ifndef NEAR +#ifdef BIT16 +#define NEAR __near +#else +#define NEAR +#endif +#endif + +#ifndef FAR +#ifdef BIT16 +#define FAR __far +#else +#define FAR +#endif +#endif + +#ifndef HUGE +#ifdef BIT16 +#define HUGE __huge +#else +#define HUGE +#endif +#endif + +#ifndef FAST +#ifdef BIT16 +#define FAST __near __pascal +#else +#define FAST +#endif +#endif + +#ifndef FAST2 +#ifdef BIT16 +#define FAST2 __near __fastcall +#else +#define FAST2 __fastcall +#endif +#endif + +#ifndef _MI_MEMORY_DEFINED +#define _MI_MEMORY_DEFINED +typedef void HUGE * MI_MEMORY; +#endif + +#ifndef _MHANDLE_DEFINED +#define _MHANDLE_DEFINED +typedef unsigned long MHANDLE; +#endif + +#ifndef UNALIGNED +#ifndef NEEDS_ALIGNMENT +#define UNALIGNED +#else +#define UNALIGNED __unaligned +#endif +#endif + + +/* + * QDI will try to create a virtual ring buffer on disk if the pfnalloc call + * to create the buffer fails. These functions provide QDI the disk access + * features needed. + * + * These are modeled after the C run-time routines _open, _read, + * _write, _close, and _lseek. The values for the PFNOPEN oflag + * and pmode calls are those defined for _open. QDI expects error + * handling to be identical to these C run-time routines. + * + * As long as you faithfully copy these aspects, you can supply + * any functions you like! + * + * For PFNOPEN, the pszFile parameter will take on a special form for QDI's + * temporary file. The special form appears as a file named "*". Such a + * name field should be cast into the struct below, which contains the + * required file's size as shown in the RINGNAME structure below. + * + * Example open and close callbacks are provided. It is assumed that the + * client will provide more adaptive code for determining the temporary + * file's name and drive location, based on environment variables and the + * amount of free disk space. This sample code has hard-coded the actual + * path and fails if there is not enough free space. This code creates the + * file, then attempts to expand it to the requested size by writing a byte + * (any byte) at the requested size - 1. (This approach is not suitable for + * a file system which can support sparse files.) + * + * The callback routine may create this file on any path, and with any name, + * as appropriate. If the file cannot be created with the requested size, + * the PFNOPEN should fail. The file really should be placed on a local + * fixed disk. It would not be appropriate for the file to be placed on a + * compressed drive or a floppy disk. If the client has access to alternate + * memory, such as XMS or EMS, these operations could be emuluated. + * + * static int tempHandle = -1; + * + * int FAR DIAMONDAPI MyOpen(char FAR *pszFile,int oflag,int pmode) + * { + * if (*pszFile == '*') + * { + * PRINGNAME pringDescriptor; + * + * pringDescriptor = (PRINGNAME) pszFile; + * + * tempHandle = _open("C:\\qdi_temp.$$$",oflag,pmode); + * + * if (tempHandle != -1) + * { + * _lseek(tempHandle,(pringDescriptor->fileSize - 1),SEEK_SET); + * + * if (_write(tempHandle,&tempHandle,1) != 1) + * { + * _close(tempHandle); + * remove("C:\\qdi_temp.$$$"); + * tempHandle = -1; + * } + * } + * + * return(tempHandle); + * } + * else + * { + * * QDI only will call with *pszFile == '*' * + * } + * } + * + * The callback provider must watch for the corresponding PFNCLOSE call on + * the returned handle, and delete the created file after closing. (The + * file handle and file name assigned to the temporary file must be tracked; + * a close operation on that handle must be trapped, so the temporary file + * can be deleted as well.) + * + * The client does not need to worry about multiple concurrent opens of the + * temporary file, or more than a single temporary file (from QDI). + * + * int FAR DIAMONDAPI MyClose(int handle) + * { + * int result; + * + * result = _close(handle); + * + * if (handle == tempHandle) + * { + * remove("C:\\qdi_temp.$$$"); + * tempHandle = -1; + * } + * + * return(result); + * } + */ + +typedef int (FAR DIAMONDAPI *PFNOPEN) (char FAR *pszFile,int oflag,int pmode); +typedef UINT (FAR DIAMONDAPI *PFNREAD) (int hf, void FAR *pv, UINT cb); +typedef UINT (FAR DIAMONDAPI *PFNWRITE)(int hf, void FAR *pv, UINT cb); +typedef int (FAR DIAMONDAPI *PFNCLOSE)(int hf); +typedef long (FAR DIAMONDAPI *PFNSEEK) (int hf, long dist, int seektype); + +#pragma pack (1) + +typedef struct +{ + char wildName[2]; /* set to { '*', '\0' } */ + unsigned long fileSize; /* the required file size in bytes */ +} RINGNAME, FAR *PRINGNAME; + +#pragma pack () + +/* --- QDI-defined types -------------------------------------------------- */ + +/* QDI_CONTEXT_HANDLE - Handle to a QDI decompression context */ + +typedef MHANDLE QDI_CONTEXT_HANDLE; /* hmd */ + + +/*** PFNALLOC - Memory allocation function for QDI + * + * Entry: + * cb - Size in bytes of memory block to allocate + * + * Exit-Success: + * Returns !NULL pointer to memory block + * + * Exit-Failure: + * Returns NULL; insufficient memory + */ +#ifndef _PFNALLOC_DEFINED +#define _PFNALLOC_DEFINED +typedef MI_MEMORY (FAR DIAMONDAPI *PFNALLOC)(ULONG cb); /* pfnma */ +#endif + + +/*** PFNFREE - Free memory function for QDI + * + * Entry: + * pv - Memory block allocated by matching PFNALLOC function + * + * Exit: + * Memory block freed. + */ +#ifndef _PFNFREE_DEFINED +#define _PFNFREE_DEFINED +typedef void (FAR DIAMONDAPI *PFNFREE)(MI_MEMORY pv); /* pfnmf */ +#endif + +/* --- prototypes --------------------------------------------------------- */ + +/*** QDICreateDecompression - Create QDI decompression context + * + * Entry: + * pcbDataBlockMax *largest uncompressed data block size expected, + * gets largest uncompressed data block allowed + * pvConfiguration passes implementation-specific info to decompressor. + * pfnma memory allocation function pointer + * pfnmf memory free function pointer + * pcbSrcBufferMin gets max compressed buffer size + * pmdhHandle gets newly-created context's handle + * pfnopen file open function pointer (or NULL) + * pfnread file read function pointer (or don't care) + * pfnwrite file write function pointer (or don't care) + * pfnclose file close function pointer (or don't care) + * pfnseek file seek function pointer (or don't care) + * + * If NULL is provided for pfnopen, and the ring buffer cannot be + * created via pfnma, QDICreateDecompression will fail. + * + * If pmdhHandle==NULL, *pcbDataBlockMax and *pcbSrcBufferMin will be + * filled in, but no context will be created. This query will allow + * the caller to determine required buffer sizes before creating a + * context. + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * *pcbDataBlockMax, *pcbSrcBufferMin, *pmdhHandle filled in. + * + * Exit-Failure: + * MDI_ERROR_NOT_ENOUGH_MEMORY, could not allocate enough memory. + * MDI_ERROR_BAD_PARAMETERS, something wrong with parameters. + * *pcbDataBlockMax, *pcbSrcBufferMin, *pmdhHandle undefined. + */ +int FAR DIAMONDAPI QDICreateDecompression( + UINT FAR * pcbDataBlockMax, /* max uncompressed data block size */ + void FAR * pvConfiguration, /* implementation-defined */ + PFNALLOC pfnma, /* Memory allocation function ptr */ + PFNFREE pfnmf, /* Memory free function ptr */ + UINT FAR * pcbSrcBufferMin, /* gets max. comp. buffer size */ + QDI_CONTEXT_HANDLE FAR * pmdhHandle, /* gets newly-created handle */ + PFNOPEN pfnopen, /* open a file callback */ + PFNREAD pfnread, /* read a file callback */ + PFNWRITE pfnwrite, /* write a file callback */ + PFNCLOSE pfnclose, /* close a file callback */ + PFNSEEK pfnseek); /* seek in file callback */ + + +/*** QDIDecompress - Decompress a block of data + * + * Entry: + * hmd handle to decompression context + * pbSrc source buffer (compressed data) + * cbSrc compressed size of data to be decompressed + * pbDst destination buffer (for decompressed data) + * *pcbDecompressed (ptr to UINT) the expected de-compressed size + * of this data block. (same as cbSrc from the + * QCICompress() call.). + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * *pcbDecompressed has size of decompressed data in pbDst. + * Decompression context updated. + * + * Exit-Failure: + * MDI_ERROR_BAD_PARAMETERS, something wrong with parameters. + * MDI_ERROR_BUFFER_OVERFLOW, cbSrc is too small to yield the + * requested *pcbDecompressed count. cbSrc before QDIDecompressed + * should always equal *pcbResult after QCICompress(), and + * *pcbDecompressed before QDIDecompress should always equal the + * cbSrc before QCICompress(). + * MDI_ERROR_FAILED, either cbSrc is too small, *pcbDecompressed is too + * large, or *pbSrc is corrupt. + * + * Note: + * Set your cbDecompressed to the expected de-compressed size of this + * data block, then call QDIDecompress() with the address of your + * cbDecompressed. + */ +int FAR DIAMONDAPI QDIDecompress( + QDI_CONTEXT_HANDLE hmd, /* decompression context */ + void FAR * pbSrc, /* source buffer */ + UINT cbSrc, /* source data size */ + void FAR * pbDst, /* target buffer */ + UINT FAR * pcbDecompressed); /* target data size */ + + +/*** QDIResetDecompression - Reset decompression history (if any) + * + * De-compression can only be started on a block which was compressed + * immediately following a MCICreateCompression() or MCIResetCompression() + * call. This function provides notification to the decompressor that the + * next compressed block begins on a compression boundary. + * + * Entry: + * hmd - handle to decompression context + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * Decompression context reset. + * + * Exit-Failure: + * Returns MDI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI QDIResetDecompression(QDI_CONTEXT_HANDLE hmd); + + +/*** QDIDestroyDecompression - Destroy QDI decompression context + * + * Entry: + * hmd - handle to decompression context + * + * Exit-Success: + * Returns MDI_ERROR_NO_ERROR; + * Decompression context destroyed. + * + * Exit-Failure: + * Returns MDI_ERROR_BAD_PARAMETERS, invalid context handle. + */ +int FAR DIAMONDAPI QDIDestroyDecompression(QDI_CONTEXT_HANDLE hmd); + +/* --- constants ---------------------------------------------------------- */ + +/* return codes */ + +#define MDI_ERROR_NO_ERROR 0 +#define MDI_ERROR_NOT_ENOUGH_MEMORY 1 +#define MDI_ERROR_BAD_PARAMETERS 2 +#define MDI_ERROR_BUFFER_OVERFLOW 3 +#define MDI_ERROR_FAILED 4 +#define MDI_ERROR_CONFIGURATION 5 + +/* --- Quantum configuration details ------------------------------------- */ + +/*** Quantum pvConfiguration structure + * + * For the Quantum decompressor, two parameters are configurable, the + * "window bits", which defines the size of the buffer needed by the + * the decompressor (must match the value used to compress), and the CPU + * type, which controls whether 386 opcodes will be used or not. If + * "unknown" is provided for the fCPUtype, QDI will attempt to determine + * the CPU type itself, which could fail or produce system faults on + * non-DOS platforms (like Windows.) Windows apps should use GetWinFlags() + * or a similiar method, and never pass "unknown". + * + * pvConfiguration points to this structure. + */ + +#pragma pack (1) + +typedef struct { + int WindowBits; /* log2(buffersize), 10..21 (1K..2M) */ + int fCPUtype; /* controls internal code selection */ +} QUANTUMDECOMPRESS; /* qdec */ + +#pragma pack () + +typedef QUANTUMDECOMPRESS *PQUANTUMDECOMPRESS; /* pqdec */ +typedef QUANTUMDECOMPRESS FAR *PFQUANTUMDECOMPRESS; /* pfqdec */ + +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * QDI_CPU_UNKNOWN detection does *not* work when running under Windows + * in 286 protected mode! Call GetWinFlags() to determine + * the CPU type and pass it explicitly! + */ + +#define QDI_CPU_UNKNOWN (-1) /* internally determined */ + +/* + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +#define QDI_CPU_80286 (0) /* '286 opcodes only */ +#define QDI_CPU_80386 (1) /* '386 opcodes used */ +#define QDI_CPU_CONSERVATIVE (QDI_CPU_80286) + +/* ----------------------------------------------------------------------- */ diff --git a/private/windows/diamond/quantum/qdi.nt/makefile b/private/windows/diamond/quantum/qdi.nt/makefile new file mode 100644 index 000000000..8fe92222f --- /dev/null +++ b/private/windows/diamond/quantum/qdi.nt/makefile @@ -0,0 +1,2 @@ +!include $(NTMAKEENV)\makefile.def +
\ No newline at end of file diff --git a/private/windows/diamond/quantum/qdi.nt/sources b/private/windows/diamond/quantum/qdi.nt/sources new file mode 100644 index 000000000..be6d54685 --- /dev/null +++ b/private/windows/diamond/quantum/qdi.nt/sources @@ -0,0 +1,15 @@ +MAJORCOMP=diamond +MINORCOMP=quantum_qdi + +TARGETNAME=qdi +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +C_DEFINES=-DBIT_CONSTANTS -DUNPAQ -DUNPAQLIB + +SOURCES=..\qdi.c \ + ..\dcomp.c \ + ..\arith.c \ + ..\lz.c + +UMTYPE=console diff --git a/private/windows/diamond/quantum/qdi_int.h b/private/windows/diamond/quantum/qdi_int.h new file mode 100644 index 000000000..dab9ad01b --- /dev/null +++ b/private/windows/diamond/quantum/qdi_int.h @@ -0,0 +1,35 @@ +/* + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * QDI_INT.H: Quantum Decompression Interface private data + * + * History: + * 20-Jun-1994 msliger Initial version. + * 18-Aug-1994 msliger Added CPU type. + */ + +/* --- QDI context structure ---------------------------------------------- */ + +typedef ULONG SIGNATURE; /* structure signature */ + +struct QDI_CONTEXT /* private structure */ +{ + SIGNATURE signature; /* for validation */ + PFNALLOC pfnAlloc; /* where the alloc() is */ + PFNFREE pfnFree; /* where the free() is */ + PFNOPEN pfnOpen; /* open a file callback or NULL */ + PFNREAD pfnRead; /* read a file callback */ + PFNWRITE pfnWrite; /* write a file callback */ + PFNCLOSE pfnClose; /* close a file callback */ + PFNSEEK pfnSeek; /* seek in file callback */ + UINT cbDataBlockMax; /* promised max data size */ + UINT fCPUtype; /* CPU we're running on, QDI_CPU_xxx */ +}; + +typedef struct QDI_CONTEXT FAR *PMDC_CONTEXT; /* a pointer to one */ + +extern PMDC_CONTEXT lastContext; /* needed for memory callbacks */ + +/* ------------------------------------------------------------------------ */ diff --git a/private/windows/diamond/quantum/quantum.h b/private/windows/diamond/quantum/quantum.h new file mode 100644 index 000000000..8de238c95 --- /dev/null +++ b/private/windows/diamond/quantum/quantum.h @@ -0,0 +1,71 @@ +// QUANTUM.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __QUANTUM +#define __QUANTUM + + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef int BOOL; + +#ifndef FALSE + #define FALSE 0 + #define TRUE !FALSE +#endif + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 23 + +#define FILES_MAX 1024 // maximum number of files in an archive + +#define ARCHIVE_SIGNATURE 0x5344 // 'DS', used to identify a Quantum archive + + +// The archive file header + +typedef struct + { + WORD Signature; + BYTE VersionMajor; + BYTE VersionMinor; + WORD NumFiles; + BYTE WindowBits; + BYTE CompFlags; + } AHEADER; + + +// The archive header is immediately followed by the list of files + +typedef struct + { + char *Name; + char *Alias; // if not NULL, the alias name to put in a new archive + char *Comment; + DWORD Size; + WORD Time; + WORD Date; + WORD Checksum; + } AFILE; + + +typedef struct + { + char *ArchiveName; + WORD NumFiles; + AFILE *Files; + BYTE VersionMajor; + BYTE VersionMinor; + BYTE WindowBits; // number of bits to address the history window + int CompressionLevel; + long SizeLimit; // limit of an archive size, for multiple volumes + } APARMS; + + +#endif // quantum.h diff --git a/private/windows/diamond/quantum/rtl.h b/private/windows/diamond/quantum/rtl.h new file mode 100644 index 000000000..d8dfff685 --- /dev/null +++ b/private/windows/diamond/quantum/rtl.h @@ -0,0 +1,23 @@ +// RTL.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __RTL +#define __RTL + +#include <io.h> +#include "defs.h" + +char * FAST Rtl_GetPathNameOnly( char *PathName, char *FileName ); +char * FAST Rtl_GetFileNameOnly( char *FileName ); +char * FAST Rtl_GetFileNameSuffixOnly( char *FileName ); +void FAST Rtl_DOS_to_ftime( WORD Date, WORD Time, struct ftime *FTime ); +int FAST Rtl_ftime_to_DOS( WORD *Date, WORD *Time, struct ftime *FTime ); +void * FAST Rtl_Malloc( long x ); +void FAST Rtl_Free( void *x ); + +#endif diff --git a/private/windows/diamond/quantum/view.h b/private/windows/diamond/quantum/view.h new file mode 100644 index 000000000..ad3a81104 --- /dev/null +++ b/private/windows/diamond/quantum/view.h @@ -0,0 +1,15 @@ +// VIEW.H +// +// Quantum file archiver and compressor +// Advanced data compression +// +// Copyright (c) 1993,1994 David Stafford +// All rights reserved. + +#ifndef __VIEW +#define __VIEW + + +void View( char *ArchiveName ); + +#endif // view.h diff --git a/private/windows/diamond/textfile.c b/private/windows/diamond/textfile.c new file mode 100644 index 000000000..2edab0fc7 --- /dev/null +++ b/private/windows/diamond/textfile.c @@ -0,0 +1,354 @@ +/*** textfile.c - routines to handle text files efficiently + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1991-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 29-Oct-1991 bens Initial version + * 16-Jan-1992 bens Fixed EOF bug; do not report EOF from TFEof until + * TFReadLine has encountered it! + * 12-Aug-1993 bens Lifted from STOCK.EXE win app, fleshed out error + * return information. + * 14-Aug-1993 bens Store file name, add query function + * 22-Aug-1993 bens Added perr to TFReadLine() + * 17-Feb-1994 bens Store file name in TEXTFILE structure; abstract + * read vs. read-write modes to ensure correct + * open flags are used; always open in BINARY mode. + * 23-Feb-1994 bens Added TFWriteLine() + * + * Functions: + * TFOpen - Open text file + * TFReadLine - Read line from text file + * TFEof - Test text file for end-of-file + * TFClose - Close text file + * TFGetFileName - Return name of file + * TFWriteLine - Write line to text file + */ + +#include <string.h> +#include <fcntl.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "textfile.h" + +#include "textfile.msg" + + +typedef struct { /* tf */ +#ifdef ASSERT + SIGNATURE sig; // structure signature sigTEXTFILE +#endif + char *pszFile; // Passed in file name + int hfile; // File handle used with _lopen/_lread/etc. + int cchBuf; // Buffer size + char *pch; // File buffer + char *pchRead; // Next byte to read from buffer + char *pchLast; // Last byte of buffer with valid data (or last written) + BOOL fEOF; // TRUE => file at EOF, buffer may have data! + BOOL fEOFtoRL; // TRUE => EOF has been returned to TFReadLine call + BOOL fReadOnly; // TRUE => Reading file; FALSE => Writing file + char achLine[cbTEXT_FILE_LINE_MAX]; // Line buffer +} TEXTFILE; +typedef TEXTFILE *PTEXTFILE; /* ptf */ + +#ifdef ASSERT +#define sigTEXTFILE MAKESIG('T','F','I','L') // TEXTFILE signature +#define AssertTF(ptf) AssertStructure(ptf,sigTEXTFILE); +#else // !ASSERT +#define AssertTF(ptf) +#endif // !ASSERT + + +#define PTFfromHTF(htf) ((PTEXTFILE)(htf)) +#define HTFfromPTF(ptf) ((HTEXTFILE)(ptf)) + + +/*** TFOpen - Open a text file for I/O + * + * NOTE: See textfile.h for entry/exit conditions. + */ +HTEXTFILE TFOpen(char *pszFile, TF_OPEN_MODES tfom, int cbBuffer, PERROR perr) +{ + PTEXTFILE ptf; + int hf; + int oflag; + int pmode; + + Assert(cbBuffer > 0); + switch (tfom) { + case tfREAD_ONLY: + oflag = _O_BINARY | _O_RDONLY; // No translation, R/O + pmode = 0; // We are not creating the file + break; + + case tfREAD_WRITE: + oflag = _O_BINARY | _O_RDWR | _O_CREAT; // No translation, R/W + pmode = _S_IREAD | _S_IWRITE; // Attributes when file is closed + break; + + default: + ErrSet(perr,pszTEXTFERR_INVALID_MODE,"%d%s",tfom,pszFile); + return NULL; + } + + //** Open file + hf = _open(pszFile,oflag,pmode); + + if (hf == -1) { // Open failed + ErrSet(perr,pszTEXTFERR_FILE_OPEN_FAILED,"%s",pszFile); + return NULL; + } + + //** Allocate text file structure + ptf = MemAlloc(sizeof(TEXTFILE)); + if (ptf == 0) { + ErrSet(perr,pszTEXTFERR_OUT_OF_MEMORY,"%s",pszFile); + return 0; + } + SetAssertSignature(ptf,sigTEXTFILE); + + //** Allocate input buffer + if (!(ptf->pch = MemAlloc(cbBuffer))) { + MemFree(ptf); // Free text structure + ErrSet(perr,pszTEXTFERR_OUT_OF_MEMORY,"%s",pszFile); + return 0; + } + + //** Allocate memory for copy of file name + if(!(ptf->pszFile = MemStrDup(pszFile))) { + MemFree(ptf->pch); // Free buffer + MemFree(ptf); // Free textfile structure + ErrSet(perr,pszTEXTFERR_OUT_OF_MEMORY,"%s",pszFile); + return 0; + } + + // Fill in text file structure + + ptf->hfile = hf; + ptf->cchBuf = cbBuffer; + ptf->pchRead = ptf->pch; // Force file read + ptf->pchLast = 0; // Force file read/empty for write + ptf->fEOF = FALSE; + ptf->fEOFtoRL = FALSE; + ptf->fReadOnly = (tfom == tfREAD_ONLY); // Remember open type + + return HTFfromPTF(ptf); // Text file "handle" +} + + +/*** TFReadLine - Read a line from a text file + * + * NOTE: See textfile.h for entry/exit conditions. + */ +int TFReadLine(HTEXTFILE htf, char *pBuffer, int cbBuffer, PERROR perr) +{ + PTEXTFILE ptf; + int cb; + char *pch; + + ptf = PTFfromHTF(htf); + AssertTF(ptf); + pch = pBuffer; + + //** Make sure file is in read mode + if (!ptf->fReadOnly) { + ErrSet(perr,pszTEXTFERR_READ_NOT_ALLOWED,"%s",ptf->pszFile); + return 0; // Failure + } + + //** Fill up user buffer + while (cbBuffer > 0) { + //** Get what we can out of buffer + while (ptf->pchRead <= ptf->pchLast) { // Copy chars from buffer + switch (*(ptf->pchRead)) { + + case '\r': + ptf->pchRead++; // Skip carriage return + break; + + case '\n': + ptf->pchRead++; // Skip newline + *pch++ = '\0'; // Terminate buffer + return (pch - pBuffer); // Bytes read, including NUL + + case 0x1a: // CTRL+Z ==> EOF + //** Edit ptf so that subsequent calls will return EOF + ptf->fEOF = TRUE; + ptf->pchRead = ptf->pch; + ptf->pchLast = 0; + + //** Figure out if we got any characters for this line + if (pch > pBuffer) { // Yes, at least one + *pch++ = '\0'; // Terminate buffer + // Next time we are called, we'll report EOF + } + else { // No, there was no line + *pch++ = '\0'; // Terminate buffer, to be nice + ptf->fEOFtoRL = TRUE; // Remember we said EOF + } + return (pch - pBuffer); + + default: + if (cbBuffer <= 1) { // No room for NULL terminator + *pch = '\0'; // Terminate buffer, to be nice +// BUGBUG 29-Oct-1991 bens Should we read to end-of-line? +// SetLastError(ERROR_BUFFER_OVERFLOW) + return 0; // Indicate failure? + } + cbBuffer--; // One less character to copy + *pch++ = *(ptf->pchRead)++; // Copy character + } + } + + //** Now go to file to get more data + if (ptf->fEOF) { // Nope, buffer had last of file + if (pch > pBuffer) { + //** Last line of file did not have an LF! + // We got some characters from the buffer, + // but we got here without seeing an end of line. + // So, pretend we saw one! + *pch++ = '\0'; // Terminate buffer + return (pch - pBuffer); // Bytes read, including NUL + // Next time we are called, we'll report EOF + } + else { + //** At EOF, and buffer was already empty => EOF + *pch++ = '\0'; // Terminate buffer, to be nice + ptf->fEOFtoRL = TRUE; // Note that we have indicated EOF + return 0; // At EOF + } + } + + //** Get more data from file, if available + cb = _read(ptf->hfile,ptf->pch,ptf->cchBuf); + if (cb != ptf->cchBuf) { // Did not fill buffer + ptf->fEOF = TRUE; // Mark at end of file + } + + //** Update pointers for more copying + ptf->pchRead = ptf->pch; // Start reading from front + ptf->pchLast = ptf->pch + cb - 1; // Last byte in buffer + } +} + + +/*** TFEof - Test for EOF on a text file + * + * NOTE: See textfile.h for entry/exit conditions. + */ +BOOL TFEof(HTEXTFILE htf) +{ + PTEXTFILE ptf; + + ptf = PTFfromHTF(htf); + AssertTF(ptf); + + return ptf->fEOFtoRL; // Be consistent with what TFReadLine +} + + +/*** TFClose - Close a text file + * + * NOTE: See textfile.h for entry/exit conditions. + */ +BOOL TFClose(HTEXTFILE htf) +{ + PTEXTFILE ptf; + int rc; + + ptf = PTFfromHTF(htf); + AssertTF(ptf); + + rc = _close(ptf->hfile); + MemFree(ptf->pszFile); + MemFree(ptf->pch); + ClearAssertSignature(ptf); + MemFree(ptf); + + return rc; +} + + +/*** TFGetFileName - Return name of file + * + * NOTE: See textfile.h for entry/exit conditions. + */ +char *TFGetFileName(HTEXTFILE htf) +{ + PTEXTFILE ptf; + + ptf = PTFfromHTF(htf); + AssertTF(ptf); + + return ptf->pszFile; +} + + +/*** TFWriteLine - Write a line to a text file + * + * NOTE: See textfile.h for entry/exit conditions. + */ +int TFWriteLine(HTEXTFILE htf, char *pBuffer, int cbBuffer, PERROR perr) +{ +#if 1 //!UNIMPLEMENTED + + return 0; // Failure + +#else // UNIMPLEMENTED + PTEXTFILE ptf; + int cb; + char *pch; + char *pchLast; + + ptf = PTFfromHTF(htf); + AssertTF(ptf); + + //** Make sure file is in write mode + if (ptf->fReadOnly) { + ErrSet(perr,pszTEXTFERR_WRITE_NOT_ALLOWED,"%s",ptf->pszFile); + return 0; // Failure + } + + //** Translate \r and \n to \r\n + pch=ptf->achLine; // First char in work buffer + pchLast = ptf->achLine + sizeof(ptf->achLine) - 1; // Last char in buffer + while (*pchBuffer && pch<pchLast) { + switch (*pchBuffer) { + + case '\n': + *pch++ = '\r'; // Insert \r before \n + // Fall through to copy \n! + default: + *pch++ = *pchBuffer++; // Copy character + } + } +//BUGBUG 23-Feb-1994 bens TFWriteLine() check for line overflow + + //** Move translated text to buffer + cb = strlen( + + while (cbBuffer > 0) { + //** Fill up buffer as much as we can + + //** Get more data from file, if available + cb = _read(ptf->hfile,ptf->pch,ptf->cchBuf); + if (cb != ptf->cchBuf) { // Did not fill buffer + ptf->fEOF = TRUE; // Mark at end of file + } + + //** Update pointers for more copying + ptf->pchRead = ptf->pch; // Start reading from front + ptf->pchLast = ptf->pch + cb - 1; // Last byte in buffer + } +#endif // UNIMPLEMENTED +} diff --git a/private/windows/diamond/textfile.h b/private/windows/diamond/textfile.h new file mode 100644 index 000000000..f6f63c67c --- /dev/null +++ b/private/windows/diamond/textfile.h @@ -0,0 +1,145 @@ +/*** textfile.h - routines to handle text files efficiently + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 29-Oct-1991 bens Initial version + * 12-Aug-1993 bens Updated types for portability + * 22-Aug-1993 bens Added perr to TFReadLine() + * 17-Feb-1994 bens Abstract read vs. read-write modes + * 23-Feb-1994 bens Added TFWriteLine() + * + * Functions: + * TFOpen - Open text file + * TFReadLine - Read line from text file + * TFEof - Test text file for end-of-file + * TFClose - Close text file + * TFGetFileName - Return name of file + */ + +#ifndef INCLUDED_TEXTFILE +#define INCLUDED_TEXTFILE 1 + +#include "error.h" + +#define cbTEXT_FILE_LINE_MAX 256 // Longest textfile line + +typedef void *HTEXTFILE; /* htf - Handle to text file */ + +/** tfREAD_ONLY, tfREAD_WRITE - Open modes for TFOpen + * + */ +typedef enum { + tfREAD_ONLY, + tfREAD_WRITE, +} TF_OPEN_MODES; /* tfom */ + + +/*** TFOpen - Open a text file for I/O + * + * Entry + * pszFile - File name. + * tfom - Read/Write/Sharing flags. + * cbBuffer - Size of read/write buffer. 0 specifies + * default size. + * perr - ERROR structure + * + * Exit-Success: + * Returns number of bytes read into buffer, *including* the + * NULL terminating character. This number may be smaller than + * the parameter cb, in which case the file has been read to its + * end. + * + * Exit-Failure: + * Returns NULL; perr filled in with error message. + */ +HTEXTFILE TFOpen(char *pszFile, TF_OPEN_MODES tfom, int cbBuffer, PERROR perr); + + +/*** TFReadLine - Read a line from a text file + * + * Read from current file position to end of line (as indicated by + * a carriage return and/or line feed). File position is advanced + * to start of next line. + * + * Entry: + * htf - Text file handle returned by TFOpen(). + * ach - Buffer to recieve line from file. The line + * terminating characters are removed, and the + * line is terminated with a NULL character. + * cb - Size of buffer, in bytes, on input. + * perr - ERROR structure + * + * Exit-Success: + * Returns number of bytes read into buffer, *including* the + * NULL terminating character. + * + * Exit-Failure: + * Returns 0; + * If TFEof() returns TRUE, then file is at end. + * If TFEof() returns FALSE, then perr is filled in with error. + * + * NOTE: + * A sequence of zero or more carriage returns ('\r') followed by + * a single line feed ('\a') is interpreted as a line separator. + * + * Carriage returns embedded in a line are ignored. + */ +int TFReadLine(HTEXTFILE htf, char *pBuffer, int cbBuffer, PERROR perr); + + +/*** TFEof - Test for EOF on a text file + * + * NOTE: Does not return TRUE until TFReadLine has encountered EOF! + * + * Entry: + * htf - Text file handle returned by TFOpen(). + * + * Exit-Success: + * Returns TRUE if at EOF. + * Returns FALSE if not at EOF. + * + * Exit-Failure: + * Returns FALSE. htf was invalid. + */ +BOOL TFEof(HTEXTFILE htf); + + +/*** TFClose - Close a text file + * + * Close file opened by TFOpen. + * + * Entry: + * htf - Text file handle returned by TFOpen(). + * + * Exit-Success: + * Returns TRUE. + * + * Exit-Failure: + * Returns FALSE. htf was invalid. + */ +BOOL TFClose(HTEXTFILE htf); + + +/*** TFGetFileName - Return name of file + * + * Entry: + * htf - Text file handle returned by TFOpen(). + * + * Exit-Success: + * Returns pointer to file name. + * + * Exit-Failure: + * Returns NULL. htf was invalid. + */ +char *TFGetFileName(HTEXTFILE htf); + + +// BOOL TFFlush(HTEXTFILE htf); + +#endif // !INCLUDED_TEXTFILE diff --git a/private/windows/diamond/textfile.msg b/private/windows/diamond/textfile.msg new file mode 100644 index 000000000..5b1684776 --- /dev/null +++ b/private/windows/diamond/textfile.msg @@ -0,0 +1,17 @@ +/*** textfile.msg - displayable strings for Text File Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 14-Aug-1993 bens Initial version + * 17-Feb-1994 bens Add more error messages + * 23-Feb-1994 bens Add error messages for TFWriteLine + */ + +#define pszTEXTFERR_FILE_OPEN_FAILED "Could not open file %1" +#define pszTEXTFERR_INVALID_MODE "Invalid open mode %1 for file %2" +#define pszTEXTFERR_OUT_OF_MEMORY "Out of memory opening file: %1" +#define pszTEXTFERR_READ_NOT_ALLOWED "Tried to read from write-only file: %1" +#define pszTEXTFERR_WRITE_NOT_ALLOWED "Tried to write to read-only file: %1" diff --git a/private/windows/diamond/tools/diamond.rc b/private/windows/diamond/tools/diamond.rc new file mode 100644 index 000000000..c8172eccf --- /dev/null +++ b/private/windows/diamond/tools/diamond.rc @@ -0,0 +1,12 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Microsoft\256 Setup Compression Utility" + +#define VER_INTERNALNAME_STR "diamond.exe" +#define VER_ORIGINALFILENAME_STR "diamond.exe" + +#include <common.ver> + diff --git a/private/windows/diamond/tools/diamwrap.c b/private/windows/diamond/tools/diamwrap.c new file mode 100644 index 000000000..0f27f54de --- /dev/null +++ b/private/windows/diamond/tools/diamwrap.c @@ -0,0 +1,821 @@ +/* + + xx.xx.94 TedM Created. + 10.31.94 JoeHol Added -b switch to help notify of files that + have had bug fixes back-out, eg. since media + build group uses -d switch, we need to know + if a file is now "older" than before so we can + also take the "bad newer" file out of the media. + + Also, verifies src and dst by DOS date and time. + + 11.01.94 JoeHol -l logs to chk.log, not compress.log. + +*/ + + + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <process.h> + + +typedef struct _SOURCEFILE { + struct _SOURCEFILE *Next; + WIN32_FIND_DATA FileData; +} SOURCEFILE, *PSOURCEFILE; + +PSOURCEFILE SourceFileList; +DWORD SourceFileCount; + +PSTR SourceSpec,TargetDirectory; +BOOL UpdateTargets; +BOOL bNotifyBackedOutFile; +BOOL bChkCompress; + +HANDLE NulHandle; + +CRITICAL_SECTION SourceListCritSect; +CRITICAL_SECTION ConsoleCritSect; + +FILE * logFile; + +DWORD ThreadCount; + +#define MAX_EXCLUDE_EXTENSION 100 +PSTR ExcludeExtension[MAX_EXCLUDE_EXTENSION]; +unsigned ExcludeExtensionCount; + + +PVOID +MALLOC( + IN DWORD Size + ) +{ + return((PVOID)LocalAlloc(LMEM_FIXED,Size)); +} + +VOID +FREE( + IN PVOID Block + ) +{ + LocalFree((HLOCAL)Block); +} + + +BOOL +AddExtensionToExclude( + IN PSTR Extension + ) +{ + // + // Make sure it starts with a dot. + // + if((*Extension != '.') || (lstrlen(Extension) < 2)) { + return(FALSE); + } + + if(ExcludeExtensionCount < MAX_EXCLUDE_EXTENSION) { + ExcludeExtension[ExcludeExtensionCount++] = Extension; + } else { + printf("Warning: exclude extension %s ignored (%u max).\n",Extension,MAX_EXCLUDE_EXTENSION); + } + + return(TRUE); +} + + +BOOL +ParseArguments( + IN int argc, + IN PSTR *argv + ) +{ + HANDLE FindHandle; + WIN32_FIND_DATA FindData; + + while(argc--) { + + if((**argv == '-') || (**argv == '/')) { + + switch((*argv)[1]) { + + case 'b': + case 'B': + + if(bNotifyBackedOutFile) { + return(FALSE); + } + bNotifyBackedOutFile = TRUE; + break; + + + case 'd': + case 'D': + if(UpdateTargets) { + return(FALSE); + } + UpdateTargets = TRUE; + break; + + case 'l': + case 'L': + + if(bChkCompress) { + return(FALSE); + } + bChkCompress = TRUE; + break; + + case 'm': + case 'M': + if(ThreadCount) { + return(FALSE); + } + ThreadCount = (DWORD)atoi((*argv)+2); + if(!ThreadCount) { + return(FALSE); + } + break; + + case 'x': + case 'X': + if((argc-- < 1) || !AddExtensionToExclude(*(++argv))) { + return(FALSE); + } + break; + + default: + return(FALSE); + } + + } else { + + if(SourceSpec) { + if(TargetDirectory) { + return(FALSE); + } else { + TargetDirectory = *argv; + } + } else { + SourceSpec = *argv; + } + } + + argv++; + } + + if(!TargetDirectory || !SourceSpec) { + return(FALSE); + } + + // + // Make sure target is a directory. + // + FindHandle = FindFirstFile(TargetDirectory,&FindData); + if(FindHandle == INVALID_HANDLE_VALUE) { + return(FALSE); + } + FindClose(FindHandle); + if(!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + return(FALSE); + } + + return(TRUE); +} + + +VOID +Usage( + VOID + ) +{ + printf("DIAMWRAP [-x .ext -x .ext ...] [-m#] [-d] Source Destination\n\n"); + printf(" -x .ext Exclude files with extension .ext.\n"); + printf(" -m Force use of # threads.\n"); + printf(" -d Update compressed files only if out of date.\n"); + printf(" -b Notify if file is now older - for media build.\n"); + printf(" -l Log information to chk.log, not compress.log.\n"); + printf(" Source Source file specification. Wildcards may be used.\n"); + printf(" Destination Specifies directory where renamed compressed files\n"); + printf(" will be placed.\n"); +} + + +VOID +DnpGenerateCompressedName( + IN PSTR Filename, + OUT PSTR CompressedName + ) + +/*++ + +Routine Description: + + Given a filename, generate the compressed form of the name. + The compressed form is generated as follows: + + Look backwards for a dot. If there is no dot, append "._" to the name. + If there is a dot followed by 0, 1, or 2 charcaters, append "_". + Otherwise assume there is a 3-character extension and replace the + third character after the dot with "_". + +Arguments: + + Filename - supplies filename whose compressed form is desired. + + CompressedName - receives compressed file name. + +Return Value: + + None. + + +--*/ + +{ + PSTR p,q; + + strcpy(CompressedName,Filename); + + p = strrchr(CompressedName,'.'); + q = strrchr(CompressedName,'\\'); + if(q < p) { + + // + // If there are 0, 1, or 2 characters after the dot, just append + // the underscore. p points to the dot so include that in the length. + // + if(lstrlen(p) < 4) { + lstrcat(CompressedName,"_"); + } else { + + // + // Assume there are 3 characters in the extension. So replace + // the final one with an underscore. + // + + p[3] = '_'; + } + + } else { + + // + // No dot, just add ._. + // + + lstrcat(CompressedName,"._"); + } +} + + +BOOL +GetSetTimeStamp( + IN PSTR FileName, + OUT PFILETIME CreateTime, + OUT PFILETIME AccessTime, + OUT PFILETIME WriteTime, + IN BOOL Set + ) +{ + HANDLE h; + BOOL b; + + // + // Open the file. + // + h = CreateFile( + FileName, + Set ? GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + + if(h == INVALID_HANDLE_VALUE) { + return(FALSE); + } + + b = Set + ? SetFileTime(h,CreateTime,AccessTime,WriteTime) + : GetFileTime(h,CreateTime,AccessTime,WriteTime); + + CloseHandle(h); + + return(b); +} + + +VOID +mprintf( + IN DWORD ThreadSerialNumber, + IN PSTR FormatString, + ... + ) +{ + CHAR msg[2048]; + va_list arglist; + + va_start(arglist,FormatString); + + vsprintf(msg,FormatString,arglist); + vfprintf(logFile,FormatString,arglist); // print to log file + + va_end(arglist); + + //EnterCriticalSection(&ConsoleCritSect); + + if(ThreadCount == 1) { + printf(msg); + } else { + printf("%u: %s",ThreadSerialNumber,msg); + } + + //LeaveCriticalSection(&ConsoleCritSect); +} + +BOOL +SameDosDateTime( + IN DWORD ThreadSerialNumber, + IN PSTR SourceFileName, + IN PSTR DestFileName + ) +{ + HANDLE h; + FILETIME ftSourceWriteTime; + FILETIME dftDestWriteTime; + FILETIME CreateTime, AccessTime; + FILETIME dCreateTime, dAccessTime; + + WORD srcDate, dstDate, srcTime, dstTime; // DOS versions. + + // + // Open the source file. + // + h = CreateFile( + SourceFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + + if(h == INVALID_HANDLE_VALUE) { + mprintf ( ThreadSerialNumber, "ERROR CreateFile: %s, gle = %ld\n", + SourceFileName, GetLastError() ); + return(FALSE); + } + + GetFileTime(h,&CreateTime,&AccessTime,&ftSourceWriteTime); + + CloseHandle(h); + + FileTimeToDosDateTime ( &ftSourceWriteTime, &srcDate, &srcTime ); + + + // + // Open the destination file. + // + h = CreateFile( + DestFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + + if(h == INVALID_HANDLE_VALUE) { + mprintf ( ThreadSerialNumber, + "ERROR CreateFile: dst %s, gle = %ld\n", + DestFileName,GetLastError()); + return(FALSE); + } + + GetFileTime(h,&dCreateTime,&dAccessTime,&dftDestWriteTime); + + CloseHandle(h); + + FileTimeToDosDateTime ( &dftDestWriteTime, &dstDate, &dstTime ); + + + // Compare that the DOS date and times are the same. + // + if ( (srcDate != dstDate) || (srcTime != dstTime) ) { + + mprintf ( ThreadSerialNumber, "ERROR: %s = %x-%x, %s = %x-%x\n", + + SourceFileName, + srcDate, srcTime, + DestFileName, + dstDate, dstTime ); + + return (FALSE); + + } + + return (TRUE); // DOS dates and times are the same. + +} + +DWORD +WorkerThread( + IN PVOID ThreadParameter + ) +{ + PSOURCEFILE SourceFile; + DWORD ThreadSerialNumber; + CHAR FullSourceName[2*MAX_PATH]; + CHAR FullTargetName[2*MAX_PATH]; + CHAR CompressedName[2*MAX_PATH]; + CHAR CmdLine[5*MAX_PATH]; + PCHAR p; + FILETIME tCreate,tAccess,tWrite; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInfo; + BOOL b; + DWORD d; + int i; + + ThreadSerialNumber = (DWORD)ThreadParameter; + + ZeroMemory(&StartupInfo,sizeof(StartupInfo)); + StartupInfo.cb = sizeof(STARTUPINFO); + + while(1) { + + // + // Pluck the next source file off the list. + // If there is no next source file, we're done. + // + EnterCriticalSection(&SourceListCritSect); + + SourceFile = SourceFileList; + if(!SourceFile) { + LeaveCriticalSection(&SourceListCritSect); + return(TRUE); + } + SourceFileList = SourceFile->Next; + + LeaveCriticalSection(&SourceListCritSect); + + // + // Form the source file name. + // + lstrcpy(FullSourceName,SourceSpec); + + if(p = strrchr(FullSourceName,'\\')) { + p++; + } else { + p = FullSourceName; + } + + lstrcpy(p,SourceFile->FileData.cFileName); + + // + // Form full target name. + // + lstrcpy(FullTargetName,TargetDirectory); + i = lstrlen(FullTargetName); + if(FullTargetName[i-1] != '\\') { + FullTargetName[i] = '\\'; + FullTargetName[i+1] = 0; + } + + lstrcat(FullTargetName,SourceFile->FileData.cFileName); + + DnpGenerateCompressedName(FullTargetName,CompressedName); + + // + // If the update flag is set, check timestamps. + // If this fails, assume the target file does not exist. + // + if(!UpdateTargets + || !( GetSetTimeStamp(CompressedName,&tCreate,&tAccess,&tWrite,FALSE) + && (CompareFileTime(&SourceFile->FileData.ftLastWriteTime,&tWrite) <= 0))) + { + + + // + // Form the command line for the child process. + // + sprintf(CmdLine,"diamond %s %s",FullSourceName,CompressedName); + + mprintf(ThreadSerialNumber,"Start %s ==> %s\n",FullSourceName,CompressedName); + + // + // Invoke the child process. + // + b = CreateProcess( + NULL, + CmdLine, + NULL, + NULL, + FALSE, + DETACHED_PROCESS, + NULL, + NULL, + &StartupInfo, + &ProcessInfo + ); + + if(b) { + + CloseHandle(ProcessInfo.hThread); + + // + // Wait for the child process to terminate and get its + // return code. + // + WaitForSingleObject(ProcessInfo.hProcess,INFINITE); + b = GetExitCodeProcess(ProcessInfo.hProcess,&d); + CloseHandle(ProcessInfo.hProcess); + + if(b) { + + // + // Return value of 0 means success. + // Set the time stamp on the target. + // + if(d) { + mprintf(ThreadSerialNumber,"ERROR compressing %s to %s.\n",FullSourceName,CompressedName); + //exit(d); + } else { + b = GetSetTimeStamp( + CompressedName, + &SourceFile->FileData.ftCreationTime, + &SourceFile->FileData.ftLastAccessTime, + &SourceFile->FileData.ftLastWriteTime, + TRUE + ); + + if(b) { + if ( SameDosDateTime ( ThreadSerialNumber, + FullSourceName, + CompressedName ) ) { + mprintf(ThreadSerialNumber,"Compressed %s to %s.\n",FullSourceName,CompressedName); + } else { + + mprintf(ThreadSerialNumber,"ERROR Compressed %s to %s - time stamps don't match.\n",FullSourceName,CompressedName); + + } + } else { + mprintf(ThreadSerialNumber,"ERROR Unable to set timestamp on compressed file %s.\n",CompressedName); + } + } + } else { + mprintf(ThreadSerialNumber,"ERROR Unable to get process \"%s\" termination code.\n",CmdLine); + } + } else { + mprintf(ThreadSerialNumber,"ERROR Unable to invoke \"%s\".\n",CmdLine); + } + } else { + + // If the notify-backed-out-flag is set, notify if the source + // is currently older than the destination. + // + if ( bNotifyBackedOutFile && + !SameDosDateTime ( ThreadSerialNumber, + FullSourceName, CompressedName ) ) { + + mprintf ( ThreadSerialNumber, "ERROR Remove file from media(code-backed-out): %s\n", + SourceFile->FileData.cFileName ); + } + + mprintf(ThreadSerialNumber,"%s is up to date.\n",CompressedName); + } + } +} + + +BOOL +AddSourceFile( + IN PWIN32_FIND_DATA FindData + ) +{ + static PSOURCEFILE LastFile = NULL; + PSOURCEFILE SourceFile; + unsigned i; + DWORD ExtensionLength; + DWORD FileNameLength; + + // + // Make sure this file is not of a type that is + // supposed to be excluded from the list of files + // we care about. + // + FileNameLength = lstrlen(FindData->cFileName); + for(i=0; i<ExcludeExtensionCount; i++) { + + ExtensionLength = lstrlen(ExcludeExtension[i]); + + if((FileNameLength > ExtensionLength) + && !lstrcmpi(ExcludeExtension[i],FindData->cFileName+FileNameLength-ExtensionLength)) { + return(TRUE); + } + } + + SourceFile = MALLOC(sizeof(SOURCEFILE)); + if(!SourceFile) { + printf("ERROR Insufficient memory.\n"); + return(FALSE); + } + + SourceFile->FileData = *FindData; + SourceFile->Next = NULL; + + if(LastFile) { + LastFile->Next = SourceFile; + } else { + SourceFileList = SourceFile; + } + + LastFile = SourceFile; + + SourceFileCount++; + + return(TRUE); +} + + + +BOOL +FindSourceFiles( + VOID + ) +{ + WIN32_FIND_DATA FindData; + HANDLE FindHandle; + + printf("Building list of source files...\n"); + + // + // Build a list of source files. + // + FindHandle = FindFirstFile(SourceSpec,&FindData); + if(FindHandle != INVALID_HANDLE_VALUE) { + + if(!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if(!AddSourceFile(&FindData)) { + FindClose(FindHandle); + return(FALSE); + } + } + + while(FindNextFile(FindHandle,&FindData)) { + + if(!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if(!AddSourceFile(&FindData)) { + FindClose(FindHandle); + return(FALSE); + } + } + } + + FindClose(FindHandle); + } + + if(!SourceFileCount) { + printf("ERROR Invalid source %s.\n",SourceSpec); + exit(1); + } + + return(SourceFileCount != 0); +} + + + +VOID +DoIt( + VOID + ) +{ + SYSTEM_INFO SystemInfo; + PHANDLE ThreadHandles; + DWORD i; + DWORD ThreadId; + + // + // Determine number of threads to use. + // This is based on the number of processors. + // + if(!ThreadCount) { + GetSystemInfo(&SystemInfo); + ThreadCount = SystemInfo.dwNumberOfProcessors; + } + + ThreadHandles = MALLOC(ThreadCount * sizeof(HANDLE)); + if(!ThreadHandles) { + printf("ERROR Insufficient memory.\n"); + exit(1); + } + + if(ThreadCount == 1) { + printf("Compressing %u files...\n",SourceFileCount); + } else { + printf("Compressing %u files (%u threads)...\n",SourceFileCount,ThreadCount); + } + + // + // Initialize the source file list critical section. + // + InitializeCriticalSection(&SourceListCritSect); + + // + // Create threads. + // + for(i=0; i<ThreadCount; i++) { + + ThreadHandles[i] = CreateThread( + NULL, + 0, + WorkerThread, + (PVOID)i, + 0, + &ThreadId + ); + } + + // + // Wait for the threads to terminate. + // + WaitForMultipleObjects(ThreadCount,ThreadHandles,TRUE,INFINITE); + + // + // Clean up and exit. + // + for(i=0; i<ThreadCount; i++) { + CloseHandle(ThreadHandles[i]); + } + + FREE(ThreadHandles); +} + + + +int +_CRTAPI1 +main( + IN int argc, + IN char *argv[] + ) +{ + // + // Skip argv[0] + // + argc--; + argv++; + + NulHandle = CreateFile( + "nul", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + + if(NulHandle == INVALID_HANDLE_VALUE) { + printf("ERROR Unable to open \\dev\\nul.\n"); + return(1); + } + + InitializeCriticalSection(&ConsoleCritSect); + + if(ParseArguments(argc,argv)) { + + if ( bChkCompress ) { + logFile = fopen ( "chk.log", "a" ); + } else { + logFile = fopen ( "compress.log", "a" ); + } + if ( logFile == NULL ) { + printf ( "ERROR Couldn't open logFile.\n" ); + exit(1); + } + + if(FindSourceFiles()) { + DoIt(); + } + } else { + Usage(); + } + + CloseHandle(NulHandle); + + if ( logFile ) { + fclose ( logFile ); + } + + return (0); +} + diff --git a/private/windows/diamond/tools/diamwrap.rc b/private/windows/diamond/tools/diamwrap.rc new file mode 100644 index 000000000..e5a106d7d --- /dev/null +++ b/private/windows/diamond/tools/diamwrap.rc @@ -0,0 +1,12 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Microsoft\256 Setup Compression Utility" + +#define VER_INTERNALNAME_STR "diamwrap.exe" +#define VER_ORIGINALFILENAME_STR "diamwrap.exe" + +#include <common.ver> + diff --git a/private/windows/diamond/tools/makefile b/private/windows/diamond/tools/makefile new file mode 100644 index 000000000..873e77ca9 --- /dev/null +++ b/private/windows/diamond/tools/makefile @@ -0,0 +1,13 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT +# +!INCLUDE $(NTMAKEENV)\makefile.def + + + + + + +
\ No newline at end of file diff --git a/private/windows/diamond/tools/makefile.inc b/private/windows/diamond/tools/makefile.inc new file mode 100644 index 000000000..13a80418c --- /dev/null +++ b/private/windows/diamond/tools/makefile.inc @@ -0,0 +1,4 @@ +obj\$(TARGET_DIRECTORY)\diamond.res: diamond.rc + +obj\$(TARGET_DIRECTORY)\diamwrap.res: diamwrap.rc + diff --git a/private/windows/diamond/tools/sources b/private/windows/diamond/tools/sources new file mode 100644 index 000000000..9cddc4ef9 --- /dev/null +++ b/private/windows/diamond/tools/sources @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Ted Miller (tedm) 19-Feb-1991 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=diamond +MINORCOMP=diamond + +TARGETNAME=diamond +TARGETPATH=..\obj +TARGETTYPE=LIBRARY + +SOURCES=..\asrt.c \ + ..\command.c \ + ..\error.c \ + ..\filelist.c \ + ..\fileutil.c \ + ..\filever.c \ + ..\format.c \ + ..\glist.c \ + ..\message.c \ + ..\mem.c \ + ..\misc.c \ + ..\dfparse.c \ + ..\layout.c \ + ..\textfile.c \ + ..\ttfver.c \ + ..\variable.c \ + ..\inf.c + +UMTYPE=console + +UMAPPL=diamond*diamwrap +UMRES=$(@R).res + +UMLIBS=$(BASEDIR)\public\sdk\lib\*\version.lib \ + $(BASEDIR)\public\sdk\lib\*\mci.lib \ + $(BASEDIR)\public\sdk\lib\*\mdi.lib \ + $(BASEDIR)\public\sdk\lib\*\qci.lib \ + $(BASEDIR)\public\sdk\lib\*\qdi.lib \ + $(BASEDIR)\public\sdk\lib\*\fci.lib \ + $(BASEDIR)\public\sdk\lib\*\fdi.lib \ + ..\obj\*\diamond.lib + +NTTARGETFILE0=obj\*\diamond.res \ + obj\*\diamwrap.res diff --git a/private/windows/diamond/tstmess.c b/private/windows/diamond/tstmess.c new file mode 100644 index 000000000..a0d55bc2f --- /dev/null +++ b/private/windows/diamond/tstmess.c @@ -0,0 +1,164 @@ +/*** tstmess.c - Test program for Message Manager (message.c) + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 13-Aug-1993 bens Initial version + * 14-Aug-1993 bens Added more test cases + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "types.h" +#include "asrt.h" +#include "message.h" + +#include "message.msg" // To verify error conditions + + +int ctestTotal=0; +int ctestSuccess=0; +BOOL fVerbose=1; // Default to verbose output for now + + +//** Function prototypes +FNASSERTFAILURE(fnafReport); +void chkMsg(char *pszNew, char *pszExpected); + + +int __cdecl main (int argc, char **argv) +{ +#ifdef ASSERT + ASSERTFLAGS asf; +#endif + char ach[cbMSG_MAX]; + + AssertRegisterFunc(fnafReport); // Register assertion reporter + + if (fVerbose) { + printf("TSTMESS: Test Message Manager\n"); + printf("-----------------------------\n"); + } + + //** Valid calls + + MsgSet(ach,"No parms"); + chkMsg(ach,"No parms"); + + MsgSet(ach,"A string %1","%s","is born"); + chkMsg(ach,"A string is born"); + + MsgSet(ach,"%1 is %2 months old %3.","%s%d%s","Joe",3,"today"); + chkMsg(ach,"Joe is 3 months old today."); + + MsgSet(ach,"%3 is %1 months old %2.","%d%s%s",3,"today","Joe"); + chkMsg(ach,"Joe is 3 months old today."); + + MsgSet(ach,"This is a long - %1","%ld",123456789); + chkMsg(ach,"This is a long - 123456789"); + + MsgSet(ach,"This is a float - %1","%4.2f",3.14); + chkMsg(ach,"This is a float - 3.14"); + + MsgSet(ach,"Test sizes - %1 %2 %3 %4","%hd%d%ld%s",32767,32767,32768L,"end"); + chkMsg(ach,"Test sizes - 32767 32767 32768 end"); + + //** Bad calls + +#ifdef ASSERT + //** Disable assertions in error paths + asf = AssertGetFlags(); + AssertSetFlags(asf || asfSKIP_ERROR_PATH_ASSERTS); +#endif + + MsgSet(ach,"No Format Specifier %1","foo"); + chkMsg(ach,pszMSGERR_BAD_FORMAT_SPECIFIER); + + MsgSet(ach,"No Format Specifier type %1","%%", "x","y"); + chkMsg(ach,pszMSGERR_SPECIFIER_TOO_SHORT); + + MsgSet(ach,"Bad Format Specifier %1 %2","%z%s", "x","y"); + chkMsg(ach,pszMSGERR_UNKNOWN_FORMAT_SPECIFIER); + + MsgSet(ach,"Not enough Format Specifiers %1 %2 %3","%s%s", "x","y"); + chkMsg(ach,pszMSGERR_BAD_FORMAT_SPECIFIER); + +#ifdef ASSERT + //** Restore Assertion Manager settings + AssertSetFlags(asf); +#endif + + //** Print Summary + + if (fVerbose) { + printf("-----------------\n"); + printf("Tests Passed: %3d\n",ctestSuccess); + printf("Tests FAILED: %3d\n",ctestTotal-ctestSuccess); + printf("-----------------\n"); + printf("TOTAL: %3d\n",ctestTotal); + printf("\n"); + } + + if (ctestSuccess < ctestTotal) { + printf("TSTMESS: %d FAILED out of %d test cases.\n", + ctestTotal-ctestSuccess,ctestTotal); + return 1; + } + else { + printf("TSTMESS: PASSED %d test cases.\n",ctestTotal); + return 0; + } +} + + +/*** chkMsg - check that formatted result matched expectation + * + * Entry: + * pszNew - Newly formatted message + * pszExpected - Expected result + * + * Exit-Success: + * Total tests and successful test counts updated; + * If verbose output is enabled, print details; + * + * Exit-Failure: + * Total test count updated; + * If verbose output is enabled, print details; + */ +void chkMsg(char *pszNew, char *pszExpected) +{ + ctestTotal++; + if (!strcmp(pszNew,pszExpected)) { + if (fVerbose) { + printf("%3d: pass: \"%s\"\n",ctestTotal,pszNew); + } + ctestSuccess++; + } + else { + if (fVerbose) { + printf("%3d: FAIL: \"%s\"\n",ctestTotal,pszNew); + printf(" Expected: \"%s\"\n",pszExpected); + } + } +} + + +#ifdef ASSERT +/*** fnafReport - Report assertion failure + * + * NOTE: See asrt.h for entry/exit conditions. + */ +FNASSERTFAILURE(fnafReport) +{ + printf("%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg); + exit(1); +} +#endif // ASSERT + diff --git a/private/windows/diamond/ttfver.c b/private/windows/diamond/ttfver.c new file mode 100644 index 000000000..fd87b653e --- /dev/null +++ b/private/windows/diamond/ttfver.c @@ -0,0 +1,156 @@ +/*** ttfver.c - TrueType File version query function + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1994 + * All Rights Reserved. + * + * Author: + * ACME group(?) + * NOTE: [bens] I disclaim all responsibility for all of the magic + * numbers included below! + * + * History: + * 04-Jun-1994 bens Copied from ACME project (courtesy alanr) + * + * Exported Functions: + * FGetTTFVersion - Get TrueType File version number + */ + +#include <fcntl.h> +#include <stdio.h> +#include <io.h> +#include <stdlib.h> +#include <string.h> +#include "ttfver.h" + +#define szDIGITS "0123456789" + +/*** DwExtractTTFVersionFromSz - Parse version out of TTF ver string + * + * Entry: + * szBuf - Version string from a TTF file + * Known Formats that we support: + * "MS core font: V1.00" + * "MS core font : V1.00" + * "Version 0.80" + * "1.009; 26 January 1993" + * + * Exit-Success: + * Returns non-zero version number. + * + * Exit-Failure: + * Returns 0. + */ +unsigned long DwExtractTTFVersionFromSz(char const *szBuf) +{ + int cb; + unsigned long ver; + char const *psz; + + //** Find start of first number + psz = strpbrk(szBuf,szDIGITS); + if (!psz) { // No digits in the string + return 0; // No version + } + + ver = (atol(psz) & 0xFFFF) << 16; // Major version goes in high word + + //** Skip over major version number + cb = strspn(psz,szDIGITS); + psz += cb; // Point at char after major number + if (*psz == '.') { // There is a minor number + ver += (atol(psz+1) & 0xFFFF); + } + + return ver; +} + + +/*** FGetTTFVersion - Get TTF version out of a TTF file + * + * Entry: + * szFile - Name of file to examine + * pdwVer - Pointer to ULONG to receive version: + * HIWORD gets major version + * LOWORD gets minor version + * + * Exit-Success: + * Returns TRUE; *pdwVer filled in with TTF version number + * + * Exit-Failure: + * Returns 0; + */ +int _cdecl FGetTTFVersion(char const *szFile, unsigned long *pdwVer) +{ + unsigned i; + unsigned cbRead; + char namebuf[255]; + int fp = -1; + unsigned numNames; + long libSeek; + unsigned cTables; + sfnt_OffsetTable OffsetTable; + sfnt_DirectoryEntry Table; + sfnt_NamingTable NamingTable; + sfnt_NameRecord NameRecord; + + cbRead = sizeof(OffsetTable) - sizeof(sfnt_DirectoryEntry); + if (szFile == NULL + || pdwVer == NULL + || strlen(szFile) < 5 + || _stricmp(".TTF", szFile + strlen(szFile) - 4) + || (fp = _open(szFile, O_RDONLY | O_BINARY)) == -1 + || _read(fp, &OffsetTable, cbRead) != (int)cbRead) { + goto LNoTTFVersion; + } + + cTables = (unsigned)SWAPW(OffsetTable.numOffsets); + + for (i = 0; i < cTables && i < 40; i++) + { + if (_read(fp, &Table, sizeof(Table)) != sizeof(Table)) + goto LNoTTFVersion; + + if (Table.tag == tag_NamingTable) + { + libSeek = (long)SWAPL(Table.offset); + cbRead = sizeof(NamingTable); + if (_lseek(fp, libSeek, SEEK_SET) != libSeek + || _read(fp, &NamingTable, cbRead) != (int)cbRead) + goto LNoTTFVersion; + + numNames = (unsigned)SWAPW(NamingTable.count); + while (numNames--) + { + cbRead = sizeof(NameRecord); + if (_read(fp, &NameRecord, cbRead) != (int)cbRead) + goto LNoTTFVersion; + + if (SWAPW(NameRecord.platformID) == 1 + && SWAPW(NameRecord.nameID) == 5) + { + long libNew = SWAPW(NameRecord.offset) + + SWAPW(NamingTable.stringOffset) + + SWAPL(Table.offset); + + cbRead = SWAPW(NameRecord.length); + if (_lseek(fp, libNew, SEEK_SET) != libNew + || _read(fp, namebuf, cbRead) != (int)cbRead) + goto LNoTTFVersion; + namebuf[SWAPW(NameRecord.length)] = '\0'; + + _close(fp); + *pdwVer = DwExtractTTFVersionFromSz(namebuf); + return(1); + } + } + goto LNoTTFVersion; + } + } + +LNoTTFVersion: + if (fp != -1) + _close(fp); + + return(0); +} diff --git a/private/windows/diamond/ttfver.h b/private/windows/diamond/ttfver.h new file mode 100644 index 000000000..ba19baccd --- /dev/null +++ b/private/windows/diamond/ttfver.h @@ -0,0 +1,60 @@ +/*** ttfver.h - Definitions for getting TrueType File version + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * ACME group(?) + * + * History: + * 04-Jun-1994 bens Copied from ACME project (courtesy alanr) + * + * Exported Functions: + * FGetTTFVersion - Get TrueType File version number + */ + +typedef unsigned short uint16; +typedef long int32; +typedef unsigned long uint32; + +#define SWAPW(a) (unsigned short)(((unsigned char)((a) >> 8)) | ((unsigned char)(a) << 8)) +#define SWAPL(a) ((((a)&0xff) << 24) | (((a)&0xff00) << 8) | (((a)&0xff0000) >> 8) | ((a) >> 24)) + +typedef long sfnt_TableTag; + +#define tag_NamingTable 0x656d616e /* 'name' */ + +typedef struct { + sfnt_TableTag tag; + uint32 checkSum; + uint32 offset; + uint32 length; +} sfnt_DirectoryEntry; + +typedef struct { + int32 version; /* 0x10000 (1.0) */ + uint16 numOffsets; /* number of tables */ + uint16 searchRange; /* (max2 <= numOffsets)*16 */ + uint16 entrySelector; /* log2 (max2 <= numOffsets) */ + uint16 rangeShift; /* numOffsets*16-searchRange*/ + sfnt_DirectoryEntry table[1]; /* table[numOffsets] */ +} sfnt_OffsetTable; + +typedef struct { + uint16 platformID; + uint16 specificID; + uint16 languageID; + uint16 nameID; + uint16 length; + uint16 offset; +} sfnt_NameRecord; + +typedef struct { + uint16 format; + uint16 count; + uint16 stringOffset; +} sfnt_NamingTable; + + +int _cdecl FGetTTFVersion(char const * szFile, unsigned long * pdwVer); diff --git a/private/windows/diamond/types.h b/private/windows/diamond/types.h new file mode 100644 index 000000000..9bb6f163a --- /dev/null +++ b/private/windows/diamond/types.h @@ -0,0 +1,60 @@ +/*** types.h - Convenient type definitions + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * History: + * 10-Aug-1993 bens Initial version + * 15-Aug-1993 bens Added USHORT, ULONG + * 23-Mar-1994 bens Added DWORD, changed NULL to void* + * 01-Apr-1994 bens UNALIGNED - define NEEDS_ALIGNMENT for RISC + */ + +#ifndef INCLUDED_TYPES +#define INCLUDED_TYPES 1 + +typedef int BOOL; /* f */ +typedef unsigned char BYTE; /* b */ +typedef unsigned short USHORT; /* us */ +typedef unsigned short WORD; /* w */ +typedef unsigned int UINT; /* ui */ +typedef unsigned long ULONG; /* ul */ +typedef unsigned long DWORD; /* dw */ + + +#ifdef _DEBUG +// don't hide statics from map during debugging +#define STATIC +#else // !_DEBUG +#define STATIC static +#endif // !_DEBUG + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifdef NEEDS_ALIGNMENT + +#ifndef UNALIGNED +#define UNALIGNED __unaligned +#endif + +#else // !NEEDS_ALIGNMENT + +#ifndef UNALIGNED +#define UNALIGNED +#endif + +#endif // !NEEDS_ALIGNMENT + +#endif // !INCLUDED_TYPES + diff --git a/private/windows/diamond/variable.c b/private/windows/diamond/variable.c new file mode 100644 index 000000000..c227f1ab1 --- /dev/null +++ b/private/windows/diamond/variable.c @@ -0,0 +1,690 @@ +/*** variable.c - Variable Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 20-Aug-1993 bens Use MemStrDup to simplify VarCreate, VarSet + * 21-Aug-1993 bens Support multiple lists (no static data!) + * 08-Feb-1994 bens Set var.pvlist! Fix error cleanup in VarCreate. + * 03-Jun-1994 bens Add vflDEFINE support; Get/SetFlags + * + * Exported Functions: + * VarCloneList - Create an exact copy of a variable list + * VarCreate - Create a variable + * VarCreateList - Create a list of variables + * VarDelete - Delete existing variable + * VarDestroyList - Destroy a list of variables + * VarFind - See if variable exists + * VarFirstVar - Get first variable from list + * VarGetBool - Get value of boolean variable + * VarGetFlags - Get variable flags + * VarGetInt - Get value of int variable + * VarGetLong - Get value of long variable + * VarGetName - Get name of variable + * VarGetString - Get value of string variable + * VarNextVar - Get next variable + * VarSet - Set value of a variable (create if necessary) + * VarSetLong - Set long variable value (create if necessary) + * VarSetFlags - Set variable flags + * + * Internal Functions: + * findVar - See if variable exists + * isValidVarName - Checks validity of variable name + * setVarValue - Validate value, then update variable + */ + +#include <string.h> +#include <stdlib.h> + +#include "types.h" +#include "asrt.h" +#include "error.h" +#include "mem.h" +#include "message.h" +#include "variable.h" + +#include "variable.msg" + +//** Empty definition, to avoid "chicken and the egg" problem +typedef struct VARLIST_t VARLIST; /* var */ +typedef VARLIST *PVARLIST; /* vlist */ + +typedef struct VARIABLE_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigVARIABLE +#endif + char *pszName; // variable name + char *pszValue; // current value + VARTYPE vtype; // variable type + VARFLAGS vfl; // special flags + PFNVCVALIDATE pfnvcv; // validation function + struct VARIABLE_t *pvarNext; // next variable + struct VARIABLE_t *pvarPrev; // previous variable + PVARLIST pvlist; // List containing this variable +} VARIABLE; /* var */ +typedef VARIABLE *PVARIABLE; /* pvar */ + +#ifdef ASSERT +#define sigVARIABLE MAKESIG('V','A','R','$') // VARIABLE signature +#define AssertVar(pvar) AssertStructure(pvar,sigVARIABLE); +#else // !ASSERT +#define AssertVar(pvar) +#endif // !ASSERT + + +typedef struct VARLIST_t { +#ifdef ASSERT + SIGNATURE sig; // structure signature sigVARLIST +#endif + PVARIABLE pvarHead; + PVARIABLE pvarTail; +} VARLIST; /* vlist */ +//typedef VARLIST *PVARLIST; /* pvlist */ +#ifdef ASSERT +#define sigVARLIST MAKESIG('V','L','S','T') // VARLIST signature +#define AssertVList(pv) AssertStructure(pv,sigVARLIST); +#else // !ASSERT +#define AssertVList(pv) +#endif // !ASSERT + + +#define HVARfromPVAR(h) ((PVARIABLE)(h)) +#define PVARfromHVAR(p) ((HVARIABLE)(p)) + +#define HVLfromPVL(h) ((PVARLIST)(h)) +#define PVLfromHVL(p) ((HVARLIST)(p)) + + +//** Function Prototypes + +PVARIABLE findVar(PVARLIST pvlist, char *pszName, PERROR perr); +BOOL isValidVarName(char *pszName, PERROR perr); +BOOL setVarValue(PVARIABLE pvar, char *pszValue, PERROR perr); + + +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// +// Exported Functions +// +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +/*** VarCreate - Create a variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARIABLE VarCreate(HVARLIST hvlist, + char *pszName, + char *pszValue, + VARTYPE vtype, + VARFLAGS vfl, + PFNVCVALIDATE pfnvcv, + PERROR perr) +{ + char ach[cbMSG_MAX]; // message formatting buffer + PVARIABLE pvar; + PVARLIST pvlist; + + pvlist = PVLfromHVL(hvlist); + AssertVList(pvlist); + + //** Make sure variable name is legal + if (!isValidVarName(pszName,perr)) { + // Use error message from isValidVarName + return NULL; + } + + //** Make sure variable does not already exist + if (findVar(pvlist,pszName, perr)) { + ErrSet(perr,pszPARERR_ALREADY_CREATED,"%s",pszName); + return NULL; + } + + //** Create VARIABLE + ErrClear(perr); // Reset error state caused by findVar() failing above! + if (!(pvar = MemAlloc(sizeof(VARIABLE)))) + goto error; + + SetAssertSignature(pvar,sigVARIABLE); + pvar->pszName = NULL; // Make sure not to free garbage! + pvar->pszValue = NULL; // Make sure not to free garbage! + pvar->vtype = vtype; + pvar->vfl = vfl; + pvar->pfnvcv = pfnvcv; + + //** Make copy of name + if (!(pvar->pszName = MemStrDup(pszName))) + goto error; + + //** Validate and copy value + if (!setVarValue(pvar,pszValue,perr)) + goto error; + + //** Link variable into list + pvar->pvarNext = NULL; // Always last on list + pvar->pvarPrev = pvlist->pvarTail; // Always points to last variable on list + + if (pvlist->pvarHead == NULL) { + pvlist->pvarHead = pvar; + pvlist->pvarTail = pvar; + } + else { + AssertVar(pvlist->pvarTail); + pvlist->pvarTail->pvarNext = pvar; // Add to end of list + pvlist->pvarTail = pvar; // New tail + } + + //** Remember which list we are on! + pvar->pvlist = pvlist; + + //** Success + return HVARfromPVAR(pvar); + +error: + if (!pvar) { + if (!(pvar->pszName)) + MemFree(pvar->pszName); + if (!(pvar->pszValue)) + MemFree(pvar->pszValue); + MemFree(pvar); + } + if (!ErrIsError(perr)) { + // Only create error message if not already present + MsgSet(ach,pszPARERR_CREATING_VARIABLE,"%s",pszName); + ErrSet(perr,pszPARERR_OUT_OF_MEMORY,"%s",ach); + } + return NULL; +} /* VarCreate */ + + +/*** VarDelete - Delete existing variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +void VarDelete(HVARIABLE hvar) +{ + PVARIABLE pvar; + PVARLIST pvlist; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + pvlist = pvar->pvlist; + AssertVList(pvlist); + AssertVar(pvlist->pvarHead); + AssertVar(pvlist->pvarTail); + + //** Free memory for variable name and value + if (pvar->pszName) + MemFree(pvar->pszName); + + if (pvar->pszValue) + MemFree(pvar->pszValue); + + //** Adjust forward list pointers + if (pvar->pvarPrev == NULL) { // At head of list + pvlist->pvarHead = pvar->pvarNext; // Remove from forward chain + } + else { // At middle or end of list + AssertVar(pvar->pvarPrev); + pvar->pvarPrev->pvarNext = pvar->pvarNext; // Remove from forward chain + } + + //** Adjust backward list pointers + if (pvar->pvarNext == NULL) { // At tail of list + pvlist->pvarTail = pvar->pvarPrev; // Remove from backward chain + } + else { + AssertVar(pvar->pvarNext); + pvar->pvarNext->pvarPrev = pvar->pvarPrev; // Remove from backward chain + } + + ClearAssertSignature(pvar); + MemFree(pvar); +} /* VarDelete * + + +/*** VarCreateList - Create a list of variables + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARLIST VarCreateList(PERROR perr) +{ + PVARLIST pvlist; + + if (!(pvlist = MemAlloc(sizeof(VARLIST)))) { + ErrSet(perr,pszPARERR_OUT_OF_MEMORY,"%s",pszPARERR_CREATING_VAR_LIST); + return NULL; + } + + SetAssertSignature(pvlist,sigVARLIST); + pvlist->pvarHead = NULL; // Empty list + pvlist->pvarTail = NULL; // Empty list + + return HVLfromPVL(pvlist); +} /* VarCreateList */ + + +/*** VarCloneList - Create an exact copy of a variable list + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARLIST VarCloneList(HVARLIST hvlist, PERROR perr) +{ + PVARIABLE pvar; + PVARLIST pvlistClone; + PVARLIST pvlist; + ERROR errDummy; // Don't care about result + + pvlist = PVLfromHVL(hvlist); + AssertVList(pvlist); + + //** Create list + if (!(pvlistClone = PVLfromHVL(VarCreateList(perr)))) { + return NULL; + } + + //** Clone variables one at a time + for (pvar=pvlist->pvarHead; pvar != NULL; pvar = pvar->pvarNext) { + AssertVar(pvar); + if (!VarCreate(HVLfromPVL(pvlistClone), + pvar->pszName, + pvar->pszValue, + pvar->vtype, + pvar->vfl, + pvar->pfnvcv, + perr)) { + //** Variable create failed + // + VarDestroyList(HVLfromPVL(pvlistClone),&errDummy); // Toss partial clone + return NULL; // Failure + } + } + + //** Clone was succesfull! + return HVLfromPVL(pvlistClone); // Success +} /* VarCloneList */ + + +/*** VarDestroyList - Destroy a list of variables + * + * NOTE: See variable.h for entry/exit conditions. + */ +BOOL VarDestroyList(HVARLIST hvlist, PERROR perr) +{ + PVARIABLE pvar; + PVARIABLE pvarNext; + PVARLIST pvlist; + + pvlist = PVLfromHVL(hvlist); + AssertVList(pvlist); + +//NOTE: +// +// Calling VarDelete() is simple, but induces a great deal of +// overhead for all of the list management. A speedier solution +// would be to have both VarDelete() and this routine call a bare-bones +// worker routine to delete the VARIABLE. However, this routine is +// lightly used by Diamond, so this optimization is not important. +// + + //** Free all of the variables + for (pvar=pvlist->pvarHead; pvar != NULL; ) { + AssertVar(pvar); + pvarNext = pvar->pvarNext; // Save next pvar before we free pvar! + VarDelete(HVARfromPVAR(pvar)); + pvar = pvarNext; // Use it + } + + ClearAssertSignature(pvlist); + + //** Free the list itself + MemFree(pvlist); + + return TRUE; +} /* VarDestroyList */ + + +/*** VarSet - Set value of a variable (create if necessary) + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARIABLE VarSet(HVARLIST hvlist, + char *pszName, + char *pszValue, + PERROR perr) +{ + HVARIABLE hvar; + PVARIABLE pvar; + + hvar = VarFind(hvlist,pszName,perr); + //** If variable does not exist, create a simple string variable + if (hvar == NULL) { // Have to create it ourselves + hvar = VarCreate(hvlist, // list + pszName, // var name + "", // default value + vtypeSTR, // var type + vflNONE, // No flags + NULL, // No validation function + perr); + if (hvar == NULL) { + return FALSE; // Could not create variable + } + } + + //** Set new value + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + if (!setVarValue(pvar,pszValue,perr)) { + return FALSE; + } + + //** Success + return HVARfromPVAR(pvar); // Success, return hvar +} /* VarSet */ + + +/*** VarSetLong - Set long variable value (create if necessary) + * + * NOTE: See variable.h for entry/exit conditions. + */ +BOOL VarSetLong(HVARLIST hvlist, + char *pszName, + long lValue, + PERROR perr) +{ + char ach[20]; // Longest long is: 123456789012 + // -1234567890. + + _ltoa(lValue,ach,10); // Create a string version + return VarSet(hvlist,pszName,ach,perr) != NULL; +} /* VarSetLong() */ + + +/*** VarFind - See if variable exists + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARIABLE VarFind(HVARLIST hvlist, + char *pszName, + PERROR perr) +{ + PVARLIST pvlist; + + pvlist = PVLfromHVL(hvlist); + AssertVList(pvlist); + + //** Make sure variable name is legal + if (!isValidVarName(pszName,perr)) { + return NULL; // isValidVarName sets perr + } + + //** Find variable + return HVARfromPVAR(findVar(pvlist, pszName, perr)); +} + + +/*** VarGetBool - Get value of boolean variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +BOOL VarGetBool(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return atoi(pvar->pszValue); +} /* VarFind */ + + +/*** VarGetInt - Get value of int variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +int VarGetInt(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return atoi(pvar->pszValue); +} + + +/*** VarGetLong - Get value of long variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +long VarGetLong(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return atol(pvar->pszValue); +} + + +/*** VarGetString - Get value of string variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +char *VarGetString(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return pvar->pszValue; +} + + +/*** VarSetFlags - Set variable flags + * + * NOTE: See variable.h for entry/exit conditions. + */ +void VarSetFlags(HVARIABLE hvar, + VARFLAGS vfl) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + pvar->vfl = vfl; +} /* VarSetFlags() */ + + +/*** VarGetFlags - Get variable flags + * + * NOTE: See variable.h for entry/exit conditions. + */ +VARFLAGS VarGetFlags(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return pvar->vfl; +} /* VarGetFlags() */ + + +/*** VarFirstVar - Get first variable from list + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARIABLE VarFirstVar(HVARLIST hvlist) +{ + PVARLIST pvlist; + + pvlist = PVLfromHVL(hvlist); + AssertVList(pvlist); + + return HVARfromPVAR(pvlist->pvarHead); +} /* VarFirstVar() */ + + +/*** VarNextVar - Get next variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +HVARIABLE VarNextVar(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return HVARfromPVAR(pvar->pvarNext); +} /* VarNextVar() */ + + +/*** VarGetName - Get name of variable + * + * NOTE: See variable.h for entry/exit conditions. + */ +char *VarGetName(HVARIABLE hvar) +{ + PVARIABLE pvar; + + pvar = PVARfromHVAR(hvar); + AssertVar(pvar); + + return pvar->pszName; +} /* VarGetName() */ + + +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// +// Private Functions +// +//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +/*** isValidVarName - Checks validity of variable name + * + * Entry: + * pszName - Variable name to check + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE. + * + * Exit-Failure: + * Returns FALSE; perr filled in with error. + */ +BOOL isValidVarName(char *pszName, PERROR perr) +{ + //** Check name length + if (strlen(pszName) >= cbVAR_NAME_MAX) { + ErrSet(perr,pszPARERR_VAR_NAME_TOO_LONG,"%d%s",cbVAR_NAME_MAX,pszName); + return FALSE; + } + //** Check for invalid characters, especially chVAR! +//BUGBUG 10-Aug-1993 bens isValidVarName is incomplete + return TRUE; +} + + +/*** findVar - See if variable exists + * + * Entry: + * pvlist - Variable list + * pszName - Variable name to look for + * perr - ERROR structure + * + * Exit-Success: + * Returns PVARIABLE, if variable exists. + * + * Exit-Failure: + * Returns NULL, variable does not exist. + * ERROR structure filled in with details of error. + */ +PVARIABLE findVar(PVARLIST pvlist, char *pszName, PERROR perr) +{ + PVARIABLE pvar; + + AssertVList(pvlist); + + for (pvar=pvlist->pvarHead; pvar != NULL; pvar = pvar->pvarNext) { + AssertVar(pvar); + if (!_stricmp(pvar->pszName,pszName)) { // Got a match! + return pvar; // Return variable pointer + } + } + + //** Did not find variable + ErrSet(perr,pszPARERR_VARIABLE_NOT_FOUND,"%s",pszName); + return NULL; +} + + +/*** setVarValue - Validate value, then update variable + * + * Entry: + * pvar - Variable to update + * pszValue - New value + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; variable updated + * + * Exit-Failure: + * Returns FALSE, variable not updated. + * ERROR structure filled in with details of error. + */ +BOOL setVarValue(PVARIABLE pvar, char *pszValue, PERROR perr) +{ + char achValue[cbVAR_VALUE_MAX]; + char achMsg[cbMSG_MAX]; + char *psz; + + AssertVar(pvar); + + //** Check value length + if (strlen(pszValue) >= cbVAR_VALUE_MAX) { + ErrSet(perr,pszPARERR_VALUE_TOO_LONG,"%d%s", + cbVAR_VALUE_MAX,pvar->pszName); + return FALSE; + } + + //** Check if new value is OK + if (pvar->pfnvcv == NULL) { // No validation function + strcpy(achValue,pszValue); // Value is fine + } + else if (!(pvar->pfnvcv)(HVLfromPVL(pvar->pvlist), + pvar->pszName, + pszValue, + achValue, + perr)) { + //** Something wrong with value; validation function set perr + return FALSE; + } + + //** Set new value + psz = pvar->pszValue; // Remember original value + if (!(pvar->pszValue = MemStrDup(achValue))) { + pvar->pszValue = psz; // Restore original value + MsgSet(achMsg,pszPARERR_SETTING_VARIABLE,"%s",pvar->pszName); + ErrSet(perr,pszPARERR_OUT_OF_MEMORY,"%s",achMsg); + return FALSE; + } + + //** Free old value + if (psz != NULL) // If old value exists + MemFree(psz); // Free old value + + return TRUE; +} /* setVarValue() */ diff --git a/private/windows/diamond/variable.h b/private/windows/diamond/variable.h new file mode 100644 index 000000000..631961c90 --- /dev/null +++ b/private/windows/diamond/variable.h @@ -0,0 +1,364 @@ +/*** variable.h - Definitions for Variable Manager + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 10-Aug-1993 bens Initial version + * 21-Aug-1993 bens Added variable lists + * 11-Feb-1994 bens VarSet creates new VARIABLE if necessary + * 20-Apr-1994 bens Pass hvlist to variable validation functions + * 03-Jun-1994 bens Added list traversal, get/set flags, get name + * + * Exported Functions: + * VarCreateList - Create a list of variables + * VarCloneList - Create an exact copy of a variable list + * VarDestroyList - Destroy a list of variables + * + * VarCreate - Create a variable + * VarDelete - Delete existing variable + * VarFind - See if variable exists + * + * VarGetBool - Get value of boolean variable + * VarGetInt - Get value of int variable + * VarGetLong - Get value of long variable + * VarGetString - Get value of string variable + * + * VarSet - Set value of a variable (create if necessary) + * VarSetLong - Set long variable value (create if necessary) + * + * VarFirstVar - Get first variable from list + * VarGetFlags - Get variable flags + * VarSetFlags - Set variable flags + * VarGetName - Get name of variable + * VarNextVar - Get next variable + */ + +#ifndef INCLUDED_VARIABLE +#define INCLUDED_VARIABLE 1 + +#include "types.h" +#include "asrt.h" +#include "error.h" + +//** cbVAR_NAME_MAX - Maximum length of a variable name, including NULL +#define cbVAR_NAME_MAX 32 + +//** cbVAR_VALUE_MAX - Maximum length of a variable value, including NULL +#define cbVAR_VALUE_MAX 256 + + +typedef void * HVARLIST; /* hvlist - list of variables */ +typedef void * HVARIABLE; /* hvar - VARIABLE handle */ +typedef unsigned VARFLAGS; /* vfl - VARIABLE flags */ +#define vflNONE 0x00 // VARIABLE is not special +#define vflPERM 0x01 // VARIABLE cannot be deleted +#define vflDEFINE 0x02 // VARIABLE was .Defined +#define vflCOPY 0x04 // VARIABLE needs to be copied + +typedef enum { + vtypeBAD, // Invalid type + vtypeCHAR, // Character type + vtypeINT, // Integer + vtypeBOOL, // Boolean + vtypeLONG, // Long + vtypeSTR, // String +} VARTYPE; /* vtype - VARIABLE type */ + + +/*** PFNVCVALIDATE - Function type for VarCreate validation + *** FNVCVALIDATE - macro to help define VarCreate validation function + * + * Entry: + * hvlist - Variable list for this variable + * pszName - Variable name + * pszValue - Value to check for validity + * pszNewValue - Buffer to receive validated value + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, value is valid for variable. + * pszNewValue filled in with desired value. + * + * Exit-Failure: + * Returns FALSE, value is not valid for variable. + * ERROR structure filled in with details of error. + * + * Notes: + * (1) The validation function may reenter the Variable Manager with + * any call that does not add or remove variables. + */ +typedef BOOL (*PFNVCVALIDATE)(HVARLIST hvlist, + char *pszName, + char *pszValue, + char *pszNewValue, + PERROR perr); +#define FNVCVALIDATE(fn) BOOL fn(HVARLIST hvlist, \ + char *pszName, \ + char *pszValue, \ + char *pszNewValue, \ + PERROR perr) + + +/*** VarCreate - Create a variable + * + * Entry: + * hvlist - Variable list to check + * pszName - Variable name + * pszValue - Initial value + * vtype - Type of variable + * vfl - Special variable flags + * pfnvcv - Validation function + * perr - ERROR structure + * + * Exit-Success: + * Returns HVAR, variable is created. + * + * Exit-Failure: + * Returns NULL, cannot create variable. + * ERROR structure filled in with details of error. + */ +HVARIABLE VarCreate(HVARLIST hvlist, + char *pszName, + char *pszDefaultValue, + VARTYPE vtype, + VARFLAGS vfl, + PFNVCVALIDATE pfnvcv, + PERROR perr); + + +/*** VarCreateList - Create a list of variables + * + * Entry: + * perr - ERROR structure + * + * Exit-Success: + * Returns HVARLIST; list is created. + * + * Exit-Failure: + * Returns NULL, cannot create list; perror filled in with error. + */ +HVARLIST VarCreateList(PERROR perr); + + +/*** VarCloneList - Create an exact copy of a variable list + * + * Entry: + * hvlist - Variable list to clone + * perr - ERROR structure + * + * Exit-Success: + * Returns new HVARLIST; list was copied. + * + * Exit-Failure: + * Returns NULL, cannot copy list; perror filled in with error. + */ +HVARLIST VarCloneList(HVARLIST hvlist, PERROR perr); + + +/*** VarDestroyList - Destroy a list of variables + * + * Entry: + * hvlist - Variable list to destroy + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE; list was destroyed. + * + * Exit-Failure: + * Returns FALSE, cannot destroy list; perror filled in with error. + */ +BOOL VarDestroyList(HVARLIST hvlist, PERROR perr); + + +/*** VarSet - Set value of a variable (create if necessary) + * + * Entry: + * hvlist - Variable list + * pszName - Variable name + * pszValue - New value + * perr - ERROR structure + * + * Exit-Success: + * Returns HVARIABLE, variable is created (if necessary) and value set. + * + * Exit-Failure: + * Returns NULL, cannot set variable value. + * ERROR structure filled in with details of error. + */ +HVARIABLE VarSet(HVARLIST hvlist, + char *pszName, + char *pszValue, + PERROR perr); + + +/*** VarSetLong - Set long variable value (create if necessary) + * + * Entry: + * hvlist - Variable list + * pszName - Variable name + * lValue - New value + * perr - ERROR structure + * + * Exit-Success: + * Returns TRUE, variable is created (if necessary) and value set. + * + * Exit-Failure: + * Returns FALSE, cannot set variable value. + * ERROR structure filled in with details of error. + */ +BOOL VarSetLong(HVARLIST hvlist, + char *pszName, + long lValue, + PERROR perr); + + +/*** VarFind - See if variable exists + * + * Entry: + * hvlist - Variable list + * pszName - Variable name to look for + * perr - ERROR structure + * + * Exit-Success: + * Returns HVAR, if variable exists. + * + * Exit-Failure: + * Returns NULL, variable does not exist. + * ERROR structure filled in with details of error. + */ +HVARIABLE VarFind(HVARLIST hvlist, + char *pszName, + PERROR perr); + + +/*** VarDelete - Delete existing variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Always works, since hvar is valid. + */ +void VarDelete(HVARIABLE hvar); + + +/*** VarGetBool - Get value of boolean variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns value of variable (TRUE or FALSE) + */ +BOOL VarGetBool(HVARIABLE hvar); + + +/*** VarGetInt - Get value of int variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns value of variable (int) + * NOTE: If variable is a long, return value is undefined. + * If variable is a BOOL, return value is 0 or 1. + * If variable is a string, return value is atoi(string) + */ +int VarGetInt(HVARIABLE hvar); + + +/*** VarGetLong - Get value of long variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns value of variable (long) + * NOTE: If variable is a int, return value is cast to a long + * If variable is a BOOL, return value is 0 or 1. + * If variable is a string, return value is atol(string) + */ +long VarGetLong(HVARIABLE hvar); + + +/*** VarGetString - Get value of string variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns pointer to value of variable. + * NOTE: Caller may not modify variable value directly! + */ +char *VarGetString(HVARIABLE hvar); + + +/*** VarSetFlags - Set variable flags + * + * Entry: + * hvar - Variable handle + * vfl - New value for variable flags + * + * Exit-Success: + * Variable flags are updated. + */ +void VarSetFlags(HVARIABLE hvar, + VARFLAGS vfl); + + +/*** VarGetFlags - Get variable flags + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns variable flags. + */ +VARFLAGS VarGetFlags(HVARIABLE hvar); + + +/*** VarFirstVar - Get first variable from list + * + * Entry: + * hvlist - Variable list + * + * Exit-Success: + * Returns HVARIABLE of first variable in list. + * + * Exit-Failure: + * Returns NULL; hglist is bad or empty. + */ +HVARIABLE VarFirstVar(HVARLIST hvlist); + + +/*** VarNextVar - Get next variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns HVARIABLE of next variable following hvar in list. + * + * Exit-Failure: + * Returns NULL; no more variables + */ +HVARIABLE VarNextVar(HVARIABLE hvar); + + +/*** VarGetName - Get name of variable + * + * Entry: + * hvar - Variable handle + * + * Exit-Success: + * Returns pointer to name of variable. + * NOTE: Caller may not modify the name directly! + */ +char *VarGetName(HVARIABLE hvar); + +#endif // !INCLUDED_VARIABLE diff --git a/private/windows/diamond/variable.msg b/private/windows/diamond/variable.msg new file mode 100644 index 000000000..6ccc8c827 --- /dev/null +++ b/private/windows/diamond/variable.msg @@ -0,0 +1,25 @@ +/*** variable.msg - Displayable strings for variable.c + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 12-Aug-1993 bens Moved from strings.h + */ + +//** Error Messages + +#define pszPARERR_ALREADY_CREATED "[INTERR] Variable already created: %1" +#define pszPARERR_VAR_NAME_TOO_LONG "Variable name is longer than maximum (%1 chars): %2" + +#define pszPARERR_OUT_OF_MEMORY "Out of memory: %1" +#define pszPARERR_SETTING_VARIABLE "attempting to set variable: %1" +#define pszPARERR_CREATING_VARIABLE "attempting to create variable: %1" +#define pszPARERR_CREATING_VAR_LIST "attempting to create a variable list" + +#define pszPARERR_VALUE_TOO_LONG "Variable value is longer than maximum (%1 chars): %2" +#define pszPARERR_VARIABLE_NOT_FOUND "Undefined variable: %1" diff --git a/private/windows/diamond/wip.c b/private/windows/diamond/wip.c new file mode 100644 index 000000000..bdb27fa5a --- /dev/null +++ b/private/windows/diamond/wip.c @@ -0,0 +1,232 @@ +/*** wip.c - work in progress: testbed for new code + * + * Microsoft Confidential + * Copyright (C) Microsoft Corporation 1993-1994 + * All Rights Reserved. + * + * Author: + * Benjamin W. Slivka + * + * History: + * 23-May-1994 bens Initial version + * 24-May-1994 bens Add call to get "language" + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <windows.h> + + +/* +#define HIWORD(l) ((WORD)(l>>16)) +#define LOWORD(l) ((WORD)(l&0xFFFF)) +*/ + +typedef struct { + DWORD bits; + char *psz; +} VOS_DESCRIPTION; /* vosd */ +typedef VOS_DESCRIPTION *PVOS_DESCRIPTION; /* pvosd */ + +VOS_DESCRIPTION avosd[] = { + {VOS_DOS , "DOS" }, + {VOS_OS216 , "OS/2 16-bit"}, + {VOS_OS232 , "OS/2 32-bit"}, + {VOS_NT , "Windows NT" }, + {VOS__BASE , "NT Base API"}, + {VOS__WINDOWS16 , "Win16" }, + {VOS__PM16 , "PM16" }, + {VOS__PM32 , "PM32" }, + {VOS__WINDOWS32 , "Win32" }, +}; +#define cvosd (sizeof(avosd)/sizeof(VOS_DESCRIPTION)) + + +BOOL getVer(void *pBlock, char *pszKey, void **ppData, int *pcbData); + + +int __cdecl main(int cArg, char **ppszArg) +{ + char achName[256]; + int cb; + DWORD cbFFI; + DWORD cbFVI; + DWORD cbLang; + int cbits; + DWORD dw; + DWORD dwLangPrimary; + DWORD handle; + int i; + VS_FIXEDFILEINFO *pFFI; + char *pbFVI; + char *psz; + char *pszFile; + DWORD *pdwLang; + int rc; + + //** Check arguments + if (cArg != 2) { + printf("usage: WIP filename\n"); + exit(1); + } + pszFile = ppszArg[1]; + + //** Get size of file version info + cbFVI = GetFileVersionInfoSize(pszFile, &handle); + if (cbFVI == 0) { + rc = GetLastError(); + switch (rc) { + + /* + * I came up with this list by trial and error, they certainly + * are not spec'd in the Win32 SDK! + * 25-May-1994 bens + */ + case NO_ERROR: + case ERROR_RESOURCE_DATA_NOT_FOUND: + case ERROR_RESOURCE_TYPE_NOT_FOUND: + case ERROR_RESOURCE_NAME_NOT_FOUND: //** 8/4/94 - MSVCBOOK.DLL on Daytona beta 1/2 + case ERROR_NOT_LOCKED: //** Some 16-bit EXEs, like *.FOT + printf("%s has no version information (rc=%ld).\n", pszFile, rc); + return 0; + + default: + printf("ERROR: GetFileVersionInfoSize() on %s caused error %d\n", + pszFile,rc); + return 2; + } + } + + //** Allocate buffer for info + if (!(pbFVI = malloc(cbFVI))) { + printf("ERROR: malloc failure on file version buffer (%d bytes).\n",cbFVI); + return 2; + } + + //** Get the info + if (!GetFileVersionInfo(pszFile,handle,cbFVI,pbFVI)) { + rc = GetLastError(); + printf("ERROR: GetFileVersionInfo() on %s caused error %d\n", + pszFile,rc); + return 2; + } + + //** Display version info + printf("-- %s --\n",pszFile); + if (getVer(pbFVI,"\\",&pFFI,&cbFFI)) { + printf("FileVersion: %08x %08x\n",pFFI->dwFileVersionMS, + pFFI->dwFileVersionLS); + printf(" %d.%d.%d.%d\n", + HIWORD(pFFI->dwFileVersionMS), + LOWORD(pFFI->dwFileVersionMS), + HIWORD(pFFI->dwFileVersionLS), + LOWORD(pFFI->dwFileVersionLS)); + + printf("ProductVersion: %08x %08x\n",pFFI->dwProductVersionMS, + pFFI->dwProductVersionLS); + + //** Decode operating system type + dw = pFFI->dwFileOS; + printf("OS Environment:"); + if (dw == 0) { + printf(" UNKNOWN"); + } + else { + cbits = 0; + for (i=0; (i<cvosd) && dw; i++) { + if (dw & avosd[i].bits) { + cbits++; + if (cbits > 1) { + printf(","); + } + printf(" %s",avosd[i].psz); // print description + dw &= ~avosd[i].bits; // clear bits + } + } + } + printf("\n"); + } + + //** Get language info + if (getVer(pbFVI,"\\VarFileInfo\\Translation",&pdwLang,&cbLang)) { + printf("Charset/Lang: %04x %04x\n", + HIWORD(*pdwLang), + LOWORD(*pdwLang)); + dwLangPrimary = *pdwLang; + for (pdwLang++; + cbLang > sizeof(DWORD); + cbLang -= sizeof(DWORD), pdwLang++) { + printf(" %04x %04x\n", + HIWORD(*pdwLang), + LOWORD(*pdwLang)); + } + + //** Get version *string* + sprintf(achName,"\\StringFileInfo\\%04x%04x\\FileVersion", + HIWORD(dwLangPrimary), + LOWORD(dwLangPrimary)); + if (getVer(pbFVI,achName,&psz,&cb)) { + printf("STRING version: %s (hi,lo)\n",psz); + } + +//BUGBUG 25-May-1994 bens Win32 SDK is unclear about which halfs of the dword +// the Lang and CharSet occupy, so try both! + //** Try alternate order! + sprintf(achName,"\\StringFileInfo\\%04x%04x\\FileVersion", + LOWORD(dwLangPrimary), + HIWORD(dwLangPrimary)); + if (getVer(pbFVI,achName,&psz,&cb)) { + printf("STRING version: %s (lo,hi)\n",psz); + } + } + + //** Success + return 0; +} + +/*** getVer - Get particular piece of EXE version information + * + * Entry: + * pBlock - Block filled in by GetFileVersionInfo + * pszKey - String to pass to VerQueryValue + * ppData - Pointer to variable to receive pointer to requested + * data insided pBlock. + * pcbData - Pointer to variable to receive length of requested + * data. + * + * Exit-Success: + * Returns TRUE; *ppData and *pcbData filled in. + * + * Exit-Failure: + * Returns FALSE; Could not get requested data + */ +BOOL getVer(void *pBlock, char *pszKey, void **ppData, int *pcbData) +{ + int rc; + + if (!VerQueryValue(pBlock,pszKey,ppData,pcbData)) { + rc = GetLastError(); + switch (rc) { + + case NO_ERROR: + case ERROR_RESOURCE_DATA_NOT_FOUND: + case ERROR_RESOURCE_TYPE_NOT_FOUND: + // Skip the error message + break; + + default: + printf("ERROR: VarQueryValue() caused error %d\n",rc); + } + return FALSE; + } + + //** See if version info was there + if (*pcbData == 0) { + printf("Version key '%s' not present.\n", pszKey); + return FALSE; + } + + return TRUE; +} |