NT4/private/windows/diamond/dmftools/fcopy/fcopy.c
2020-09-30 17:12:29 +02:00

2760 lines
80 KiB
C

/////////////////////////////////////////////////////////////////////////////
// //
// 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" );
}