summaryrefslogtreecommitdiffstats
path: root/private/windows/diamond
diff options
context:
space:
mode:
Diffstat (limited to 'private/windows/diamond')
-rw-r--r--private/windows/diamond/asrt.c182
-rw-r--r--private/windows/diamond/asrt.h304
-rw-r--r--private/windows/diamond/asrt.msg22
-rw-r--r--private/windows/diamond/chuck/asrt.c178
-rw-r--r--private/windows/diamond/chuck/asrt.h304
-rw-r--r--private/windows/diamond/chuck/buildcab.c1078
-rw-r--r--private/windows/diamond/chuck/buildcab.h313
-rw-r--r--private/windows/diamond/chuck/buildfol.c764
-rw-r--r--private/windows/diamond/chuck/cabinet.h380
-rw-r--r--private/windows/diamond/chuck/checksum.c61
-rw-r--r--private/windows/diamond/chuck/checksum.h25
-rw-r--r--private/windows/diamond/chuck/dirs2
-rw-r--r--private/windows/diamond/chuck/erf.c25
-rw-r--r--private/windows/diamond/chuck/erf.h27
-rw-r--r--private/windows/diamond/chuck/fci.c319
-rw-r--r--private/windows/diamond/chuck/fci.nt/makefile2
-rw-r--r--private/windows/diamond/chuck/fci.nt/sources39
-rw-r--r--private/windows/diamond/chuck/fci_int.h390
-rw-r--r--private/windows/diamond/chuck/fdi.c1626
-rw-r--r--private/windows/diamond/chuck/fdi.nt/makefile2
-rw-r--r--private/windows/diamond/chuck/fdi.nt/sources37
-rw-r--r--private/windows/diamond/chuck/fdi_int.h917
-rw-r--r--private/windows/diamond/chuck/makefil020
-rw-r--r--private/windows/diamond/chuck/nt/fci.h2
-rw-r--r--private/windows/diamond/chuck/nt/fdi.h2
-rw-r--r--private/windows/diamond/chuck/resource.h18
-rw-r--r--private/windows/diamond/chuck/types.h192
-rw-r--r--private/windows/diamond/command.c76
-rw-r--r--private/windows/diamond/command.h194
-rw-r--r--private/windows/diamond/crc32.c71
-rw-r--r--private/windows/diamond/crc32.h113
-rw-r--r--private/windows/diamond/ddump.c787
-rw-r--r--private/windows/diamond/ddump.msg57
-rw-r--r--private/windows/diamond/dfparse.c2140
-rw-r--r--private/windows/diamond/dfparse.h376
-rw-r--r--private/windows/diamond/dfparse.msg481
-rw-r--r--private/windows/diamond/diamond.c4159
-rw-r--r--private/windows/diamond/diamond.msg231
-rw-r--r--private/windows/diamond/dirs1
-rw-r--r--private/windows/diamond/dmfon.h26
-rw-r--r--private/windows/diamond/dmftools/dmffmt/dmffmt.c199
-rw-r--r--private/windows/diamond/dmftools/dmffmt/makefile6
-rw-r--r--private/windows/diamond/dmftools/dmffmt/sources31
-rw-r--r--private/windows/diamond/dmftools/dmfimage/dmfimage.c490
-rw-r--r--private/windows/diamond/dmftools/dmfimage/makefile1
-rw-r--r--private/windows/diamond/dmftools/dmfimage/precomp.h7
-rw-r--r--private/windows/diamond/dmftools/dmfimage/sources17
-rw-r--r--private/windows/diamond/dmftools/dmfwp/dmfwp.c92
-rw-r--r--private/windows/diamond/dmftools/dmfwp/makefile6
-rw-r--r--private/windows/diamond/dmftools/dmfwp/sources31
-rw-r--r--private/windows/diamond/dmftools/fcopy/bootsect.h135
-rw-r--r--private/windows/diamond/dmftools/fcopy/dmffmt.c106
-rw-r--r--private/windows/diamond/dmftools/fcopy/dmffmt.h6
-rw-r--r--private/windows/diamond/dmftools/fcopy/fcopy.c2759
-rw-r--r--private/windows/diamond/dmftools/fcopy/makefile6
-rw-r--r--private/windows/diamond/dmftools/fcopy/precomp.h6
-rw-r--r--private/windows/diamond/dmftools/fcopy/sources20
-rw-r--r--private/windows/diamond/error.c64
-rw-r--r--private/windows/diamond/error.h85
-rw-r--r--private/windows/diamond/extract.c2520
-rw-r--r--private/windows/diamond/extract.msg605
-rw-r--r--private/windows/diamond/filelist.c358
-rw-r--r--private/windows/diamond/filelist.h219
-rw-r--r--private/windows/diamond/filelist.msg65
-rw-r--r--private/windows/diamond/filetype.c615
-rw-r--r--private/windows/diamond/fileutil.c991
-rw-r--r--private/windows/diamond/fileutil.h448
-rw-r--r--private/windows/diamond/fileutil.msg225
-rw-r--r--private/windows/diamond/filever.c217
-rw-r--r--private/windows/diamond/filever.h61
-rw-r--r--private/windows/diamond/filever.msg19
-rw-r--r--private/windows/diamond/fixchg.c199
-rw-r--r--private/windows/diamond/fixchg.h11
-rw-r--r--private/windows/diamond/format.c185
-rw-r--r--private/windows/diamond/format.h87
-rw-r--r--private/windows/diamond/format.msg32
-rw-r--r--private/windows/diamond/funlist.h141
-rw-r--r--private/windows/diamond/glist.c777
-rw-r--r--private/windows/diamond/glist.h296
-rw-r--r--private/windows/diamond/glist.msg21
-rw-r--r--private/windows/diamond/inf.c2738
-rw-r--r--private/windows/diamond/inf.h304
-rw-r--r--private/windows/diamond/inf.msg86
-rw-r--r--private/windows/diamond/layout.c53
-rw-r--r--private/windows/diamond/mem.c242
-rw-r--r--private/windows/diamond/mem.h74
-rw-r--r--private/windows/diamond/mem.msg23
-rw-r--r--private/windows/diamond/message.c472
-rw-r--r--private/windows/diamond/message.h109
-rw-r--r--private/windows/diamond/message.msg21
-rw-r--r--private/windows/diamond/misc.c151
-rw-r--r--private/windows/diamond/misc.h58
-rw-r--r--private/windows/diamond/misc.msg14
-rw-r--r--private/windows/diamond/mszip/dirs2
-rw-r--r--private/windows/diamond/mszip/mci.c305
-rw-r--r--private/windows/diamond/mszip/mci.h250
-rw-r--r--private/windows/diamond/mszip/mci.nt/makefile2
-rw-r--r--private/windows/diamond/mszip/mci.nt/sources37
-rw-r--r--private/windows/diamond/mszip/mdi.c225
-rw-r--r--private/windows/diamond/mszip/mdi.h222
-rw-r--r--private/windows/diamond/mszip/mdi.nt/makefile2
-rw-r--r--private/windows/diamond/mszip/mdi.nt/sources37
-rw-r--r--private/windows/diamond/mszip/nfmcomp.c2272
-rw-r--r--private/windows/diamond/mszip/nfmcomp.h103
-rw-r--r--private/windows/diamond/mszip/nfmdeco.c1721
-rw-r--r--private/windows/diamond/mszip/nfmdeco.h102
-rw-r--r--private/windows/diamond/qstub/makefile2
-rw-r--r--private/windows/diamond/qstub/qstub.c142
-rw-r--r--private/windows/diamond/qstub/sources10
-rw-r--r--private/windows/diamond/quantum/arc.h28
-rw-r--r--private/windows/diamond/quantum/arith.c364
-rw-r--r--private/windows/diamond/quantum/arith.h32
-rw-r--r--private/windows/diamond/quantum/bit.c201
-rw-r--r--private/windows/diamond/quantum/bit.h20
-rw-r--r--private/windows/diamond/quantum/comp.c1103
-rw-r--r--private/windows/diamond/quantum/comp.h28
-rw-r--r--private/windows/diamond/quantum/crc32.h4
-rw-r--r--private/windows/diamond/quantum/dcomp.c764
-rw-r--r--private/windows/diamond/quantum/dcomp.h32
-rw-r--r--private/windows/diamond/quantum/decomp.h30
-rw-r--r--private/windows/diamond/quantum/defs.h43
-rw-r--r--private/windows/diamond/quantum/dirs1
-rw-r--r--private/windows/diamond/quantum/dirwalk.h88
-rw-r--r--private/windows/diamond/quantum/dummy.h46
-rw-r--r--private/windows/diamond/quantum/log.h7
-rw-r--r--private/windows/diamond/quantum/lz.c719
-rw-r--r--private/windows/diamond/quantum/lz.h60
-rw-r--r--private/windows/diamond/quantum/mem.h11
-rw-r--r--private/windows/diamond/quantum/qci.c249
-rw-r--r--private/windows/diamond/quantum/qci.h268
-rw-r--r--private/windows/diamond/quantum/qci.nt/makefile2
-rw-r--r--private/windows/diamond/quantum/qci.nt/sources16
-rw-r--r--private/windows/diamond/quantum/qdi.c446
-rw-r--r--private/windows/diamond/quantum/qdi.h445
-rw-r--r--private/windows/diamond/quantum/qdi.nt/makefile2
-rw-r--r--private/windows/diamond/quantum/qdi.nt/sources15
-rw-r--r--private/windows/diamond/quantum/qdi_int.h35
-rw-r--r--private/windows/diamond/quantum/quantum.h71
-rw-r--r--private/windows/diamond/quantum/rtl.h23
-rw-r--r--private/windows/diamond/quantum/view.h15
-rw-r--r--private/windows/diamond/textfile.c354
-rw-r--r--private/windows/diamond/textfile.h145
-rw-r--r--private/windows/diamond/textfile.msg17
-rw-r--r--private/windows/diamond/tools/diamond.rc12
-rw-r--r--private/windows/diamond/tools/diamwrap.c821
-rw-r--r--private/windows/diamond/tools/diamwrap.rc12
-rw-r--r--private/windows/diamond/tools/makefile13
-rw-r--r--private/windows/diamond/tools/makefile.inc4
-rw-r--r--private/windows/diamond/tools/sources65
-rw-r--r--private/windows/diamond/tstmess.c164
-rw-r--r--private/windows/diamond/ttfver.c156
-rw-r--r--private/windows/diamond/ttfver.h60
-rw-r--r--private/windows/diamond/types.h60
-rw-r--r--private/windows/diamond/variable.c690
-rw-r--r--private/windows/diamond/variable.h364
-rw-r--r--private/windows/diamond/variable.msg25
-rw-r--r--private/windows/diamond/wip.c232
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;
+}