📄 expand.c
字号:
/* Data Decompression Program
written by Steve Zeck and David Dickman
Copyright (c) 1989-1991 by Microsoft Corporation
Microsoft Confidential
All Rights Reserved.
This program will decompress files using the Lempel-Zev compress algorithm.
A header is prepended to the file being compressed which contains a
signature block and other information, like the size of the uncompressed
file. Other compression algorithms may be added fairly easily. The
compressed file header contains tags for the algorithm used in compression
and the version of the program which compressed the file. The last file
modification date is preserved through compression and decompression.
Fragmented Compression:
----------------------
Compression of files which, even when compressed, occupy more than one
floppy disk is handled in a primitive way. Once compress runs out of disk
space during a write, it asks for a new disk, and continues compression
into a file of the same output name on the new disk. Decompressing a
fragmented output file takes more work. The compressed output files must
be appended together in their output order before decompressing the
resulting unfragmented compressed file. Of course, this requires a hard
disk or some medium with sufficient space to hold the unfragmented
compressed file.
For example, suppose a large program called "bogusxl.exe" still occupies
900Kb even when compressed. It is to be shipped on 360Kb floppy disks.
Working from a directory containing the original version of bogusxl.exe on
drive c, bogusxl.exe might be compressed into bogusxl.out with the
following command line:
C:>compress e bogusxl.exe bogusxl.out
compress will try to fit as much of the compressed data on each diskette
as possible. Assuming compress is handed empty 360Kb formatted floppy
disks, bogusxl.out will be split into three fragments: 360Kb on disk 1,
360Kb on disk 2, and 180Kb on disk 3 (in order). The compressed data file
on each disk is named bogusxl.out. You might want to rename the files on
the floppy disks. e.g., bogusxl.1 on disk 1, bogusxl.2 on disk 2, and
bogusxl.3 on disk 3. Now we need to create an unfragmented version (e.g.,
on hard drive c: on the purchaser's pc).
<working from the target directory on c:>
<insert disk 1 in drive a:>
C:>copy a:bogusxl.1 c:
<insert disk 2 in drive a:>
C:>copy a:bogusxl.2 c:
<insert disk 3 in drive a:>
C:>copy a:bogusxl.3 c:
Now to stitch together the three fragments in order.
C:>copy bogusxl.1 /b + bogusxl.2 /b + bogusxl.3 /b bogusxl.out
However, the date and time stamp of bogusxl.out no longer matches that of
the original version of bogusxl.exe. To fix this, use the 's' (set time
and date) option of compress to copy bogusxl.1's stamp to bogusxl.out:
C:>compress s bogusxl.1 bogusxl.out
Now bogusxl.out is an unfragmented compressed version of bogusxl.exe, so
it just needs to be decompressed.
C:>compress d bogusxl.out bogusxl.exe
And we can get rid of the compressed files...
C:>del bogusxl.1
C:>del bogusxl.2
C:>del bogusxl.3
C:>del bogusxl.out
So now an uncompressed copy of bogusxl.exe with matching date and time
stamp is on the user's hard drive.
(A future version of compress may try to mitigate this multi-file
rigamarole.)
****************************************************************************/
/*
Notes:
-----
Fragmented compression needs to be tidied up. e.g., a fragment number
(UCHAR) could be added to the compressed file header to help automate
fragmented decompression. The 's' command line option is an especially
klugey example of the messy fragmented compression handling.
*/
/* History:
*
* 08/07/91 M001 Added the following functionality:
* 1) Do not allow source file to overwrite itself.
* 2) If source file cannot be found, try file name with 3rd
* character of extension set = '_'.
* 3) Display status messages as each file is expanded (like
* COPY, but display status even if just expanding 1 file):
* A:\EGA.SY_ -> C:\DOS\EGA.SYS
* Each message will appear on a new line, so if multiple
* files are expanded, the screen will scroll.
* 4) Number of input parameters:
* a) = 0: Prompt for source file and destination file/path
* b) = 1: Prompt for destination file/path
* c) = 2: Source must be file name
* Destination can be file name or existing
* directory name (path)
* d) > 2: First parameters (sources) must be file names
* Last parameter (destination) must be existing
* directory name (path)
* 5) Destination file name:
* a) Can be explicitly specified when only 1 file is
* being expanded.
* b) When no destination file name is specified, the
* source file name is used as specified on command-line
* (i.e. if "EXPAND A:\EGA.SYS C:\DOS" finds A:\EGA.SY_,
* it will display the status message
* "A:\EGA.SY_ -> C:\DOS\EGA.SYS"
* and create the file C:\DOS\EGA.SYS).
*
* 11/09/89 CC Copied from compress.c and isolated
* expand features. Retained former features since we
* may want them later (see #ifdef OLD's).
*/
// Headers
///////////
#include <stdio.h>
#include <stdlib.h> /* M001 */
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <io.h>
#include <dos.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <malloc.h>
#include "expand.h"
#include "expmsgs.h"
#include <decomp.h>
// M001: Local Prototypes
//////////////////////////
static unsigned fnFileCopy(int fhDst,int fhSrc);
static BOOL PromptUser(int, char **);
static int Quit(int);
static int ProcessInput(int, char **);
static int GetString(char *, int);
static int GetSourceHandle(char *);
static BOOL IsSourceDest(char *, char *);
// Globals
///////////
static long cblInSize, // size in bytes of original file
cblOutSize = 0L; // size in bytes of output file
static int iCurMatch, // index and length of longest match
cbCurMatch; // (set by LZInsertNode())
static int leftChild[cbRingBufMax + 1], // left, right children and parents
rightChild[cbRingBufMax + 257], // (these arrays make up the binary
parent[cbRingBufMax + 1]; // search trees)
static char *pszInFile, // names of input and output files
*pszOutFile;
static UCHAR uchAlgorithm, // algorithm label
uchVersion; // version id
static int doshIn, // input and output DOS file handles
doshOut;
static int wOriginalDrive;
static int iFiles; /* M001: # of files successfully expanded */
// DOS file handles are used for file references in this module since using
// FILE *'s for file references poses a problem. fclose()'ing a file which
// was fopen()'ed in write "w" or append "a" mode stamps the file with the
// current date. This undoes the effect of copyCreateDate(). Could also get
// around this fclose() problem by first fclose()'ing the file, and
// fopen()'ing it again in read "r" mode.
//
// Using file handles also allows us to bypass stream buffering, so read's
// and write's may be done with whatever buffer size is desired. Also, the
// lower-level DOS file handle functions are faster than their stream
// counterparts.
#ifdef DBCS
/*
Test if the character is DBCS lead byte
input: c = character to test
output: TRUE if leadbyte
*/
int IsDBCSLeadByte(c)
unsigned char c;
{
static unsigned char far *DBCSLeadByteTable = NULL;
union REGS inregs,outregs;
struct SREGS segregs;
unsigned char far *p;
if (DBCSLeadByteTable == NULL)
{
inregs.x.ax = 0x6300; /* get DBCS lead byte table */
intdosx(&inregs, &outregs, &segregs);
FP_OFF(DBCSLeadByteTable) = outregs.x.si;
FP_SEG(DBCSLeadByteTable) = segregs.ds;
}
p = DBCSLeadByteTable;
while (p[0] || p[1])
{
if (c >= p[0] && c <= p[1])
return TRUE;
p += 2;
}
return FALSE;
}
/*
Check if the character point is at tail byte
input: *str = strart pointer of the string
*point = character pointer to check
output: TRUE if at the tail byte
*/
int CheckDBCSTailByte(str,point)
unsigned char *str,*point;
{
unsigned char *p;
p = point;
while (p != str)
{
p--;
if (!IsDBCSLeadByte(*p))
{
p++;
break;
}
}
return ((point - p) & 1 ? TRUE : FALSE);
}
#endif
BOOL HelpSwitchPresent(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
if (strcmp(argv[i], pszHELP_SWITCH) == 0)
return(TRUE);
return(FALSE);
} // HelpSwitchPresent()
void CatPathAndFileName(char *pszPath, char *pszFileName)
{
if (*pszFileName != '\0')
{
#ifdef DBCS
if ((! SLASH(pszPath[strlen(pszPath) - 1]) &&
(pszPath[strlen(pszPath) - 1]) != ':') ||
CheckDBCSTailByte(pszPath,&pszPath[strlen(pszPath) - 1]))
#else
if (! SLASH(pszPath[strlen(pszPath) - 1]) &&
(pszPath[strlen(pszPath) - 1]) != ':')
#endif
strcat(pszPath, CHSEPSTR);
strcat(pszPath, pszFileName);
}
} // CatPathAndFileName()
char *ExtractFileName(char *pszPath)
{
char *psz;
for (psz = pszPath; *psz != '\0'; psz++)
;
#ifdef DBCS
for ( ; psz >= pszPath && ((! SLASH(*psz) && *psz != ':') ||
CheckDBCSTailByte(pszPath,psz)) ; psz--)
#else
for ( ; psz >= pszPath && ! SLASH(*psz) && *psz != ':'; psz--)
#endif
;
return(++psz);
} // ExtractFileName()
char ExtractDrive(char *pszFileName)
{
return(pszFileName[1] == ':' ?
pszFileName[0] :
(char)('a' + wOriginalDrive));
}
// WriteOutBuf()
//
// Dumps output buffer to output (compressed) file. Prompts for new floppy
// disk if old one if full. Continues dumping to output file of same name on
// new floppy disk. The only error codes it may return are
// LZERROR_BADINHANDLE and LZERROR_BADOUTHANDLE (from copyCreateDate()).
//
int WriteOutBuf(UCHAR uch, // first character to be added to the empty
// buffer after the full buffer is written
int doshDest) // output file handle
{
unsigned ucbToWrite, // number of bytes to write from buffer
ucbWritten, // number of bytes actually written
ucbTotWritten; // total number of bytes written to output
char chDrive;
int f; // holds copyCreateDate() return value
// how much of the buffer should be written to the output file?
ucbTotWritten = ucbToWrite = (unsigned)(puchOutBuf - rguchOutBuf);
// reset pointer to beginning of buffer
puchOutBuf = rguchOutBuf;
// do not write to an output file if given the 'q' command line option
if (doshDest == NO_DOSH)
{
cblOutSize += (long)ucbTotWritten;
return((int)uch);
}
while ((ucbWritten = FWRITE(doshDest, puchOutBuf, ucbToWrite)) != ucbToWrite)
{
// ran out of disk space
if (DosRemoveable(chDrive = ExtractDrive(pszOutFile)))
do
{
// shut down the old output file
if ((f = CopyCreateDate(doshIn, doshDest)) != COPYCREATEDATE_OK)
return(f);
FCLOSE(doshDest);
printf(pszINSERT_NEW_DISK, chDrive);
getchar();
// open a new output file of the same name
} while ((doshDest = FCREATE(pszOutFile)) == -1);
else
{
// shut down the old output file
FCLOSE(doshIn);
FCLOSE(doshDest);
if (remove(pszOutFile))
printf(pszNO_DELETE_OLD, pszOutFile);
printf(pszNOT_ENOUGH_DISK, chDrive);
return(LZERROR_WRITE);
}
// check to see if some buffer data remains to be written
if (ucbWritten > 0
&& _error == 0U)
{
// account for partial writes
ucbToWrite -= ucbWritten;
puchOutBuf += ucbWritten;
}
}
cblOutSize += (long)ucbTotWritten;
// add the next character to the buffer
return((int)(*puchOutBuf++ = uch));
} // WriteOutBuf()
// WriteHdr()
//
// Writes compressed file header to output file. Could add fragment number
// to write in header as argument to WriteHdr(). The only error codes that
// may be returned are LZERROR_BADINHANDLE and LZERROR_BADOUTHANDLE.
//
// header format:
// 8 bytes --> compressed file signature
// 1 byte --> algorithm label
// 1 byte --> version id
// 4 bytes --> uncompressed file size (LSB to MSB)
//
// total = 14 bytes
//
int WriteHdr(int doshDest) // output file handle
{
int i,
f; // writeUChar return value
UCHAR uch; // temporary storage for next header byte to write
// move to beginning of output file
if (doshDest != NO_DOSH && FSEEK(doshDest, 0L, SEEK_SET) != 0L)
return((int)LZERROR_BADOUTHANDLE);
// write the compressed file signature
for (i = 0; i < cbCompSigLength; i++)
{
uch = (UCHAR)(*(szCompSig + i));
if ((f = writeUChar(uch)) != (int)(uch))
return(f);
}
// write out algorithm label and version number
if ((f = writeUChar(uchAlgorithm)) != (int)uchAlgorithm)
return(f);
if ((f = writeUChar(uchVersion)) != (int)uchVersion)
return(f);
// write out input file size (long ==> 4 bytes)
// LSB first, MSB last
uch = (UCHAR)(cblInSize & CHAR_MASK); // LSB
if ((f = writeUChar(uch)) != (int)uch)
return(f);
uch = (UCHAR)((cblInSize >> 8) & CHAR_MASK);
if ((f = writeUChar(uch)) != (int)uch)
return(f);
uch = (UCHAR)((cblInSize >> 16) & CHAR_MASK);
if ((f = writeUChar(uch)) != (int)uch)
return(f);
uch = (UCHAR)((cblInSize >> 24) & CHAR_MASK); // MSB
if ((f = writeUChar(uch)) != (int)uch)
return(f);
return(WRITEHDR_OK);
} // WriteHdr()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -