2514 lines
56 KiB
C
2514 lines
56 KiB
C
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORP., 1993-1994
|
|
*
|
|
* TITLE: REGPORTE.C
|
|
*
|
|
* VERSION: 5.00
|
|
*
|
|
* AUTHOR: Tracy Sharpe
|
|
*
|
|
* DATE: 06 Apr 1994
|
|
*
|
|
* Histry: Zeyong Xu modified it in March 1999
|
|
*
|
|
* .REG format file import and export engine routines for the Registry Editor.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
#include "regeditp.h"
|
|
#include "reg1632.h"
|
|
#include "regdef.h"
|
|
#include "regdebug.h"
|
|
#include "regporte.h"
|
|
|
|
extern BOOL g_fSaveInDownlevelFormat;
|
|
TCHAR g_ValueNameBuffer[MAXVALUENAME_LENGTH];
|
|
BYTE g_ValueDataBuffer[MAXDATA_LENGTH * sizeof(TCHAR)];
|
|
|
|
|
|
// Association between the ASCII name and the handle of the registry key.
|
|
const REGISTRY_ROOT g_RegistryRoots[] = {
|
|
TEXT("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT,
|
|
TEXT("HKEY_CURRENT_USER"), HKEY_CURRENT_USER,
|
|
TEXT("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE,
|
|
TEXT("HKEY_USERS"), HKEY_USERS,
|
|
TEXT("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG,
|
|
TEXT("HKEY_DYN_DATA"), HKEY_DYN_DATA
|
|
};
|
|
|
|
const TCHAR s_RegistryHeader[] = TEXT("REGEDIT");
|
|
|
|
const TCHAR s_OldWin31RegFileRoot[] = TEXT(".classes");
|
|
|
|
const TCHAR s_Win40RegFileHeader[] = TEXT("REGEDIT4\n\n");
|
|
|
|
//
|
|
// New header is required for version 5.0 because the version detection code
|
|
// in Win 4.0 regedit was not quite correct (See comments in ImportRegFile for
|
|
// details)
|
|
//
|
|
const WORD s_UnicodeByteOrderMark = 0xFEFF;
|
|
const TCHAR s_WinNT50RegFileHeader[] = TEXT("Windows Registry Editor Version");
|
|
const TCHAR s_WinNT50RegFileVersion[] = TEXT("5.00");
|
|
|
|
const TCHAR s_HexPrefix[] = TEXT("hex");
|
|
const TCHAR s_DwordPrefix[] = TEXT("dword:");
|
|
const TCHAR g_HexConversion[16] = {TEXT('0'), TEXT('1'), TEXT('2'), TEXT('3'), TEXT('4'),
|
|
TEXT('5'), TEXT('6'), TEXT('7'), TEXT('8'), TEXT('9'),
|
|
TEXT('a'), TEXT('b'), TEXT('c'), TEXT('d'), TEXT('e'), TEXT('f')};
|
|
const TCHAR s_FileLineBreak[] = TEXT(",\\\n ");
|
|
|
|
#define SIZE_FILE_IO_BUFFER 512
|
|
|
|
typedef struct _FILE_IO {
|
|
//
|
|
// Space for unicode/ansi conversions, assumes worst case
|
|
// where every unicode char is a double-byte char
|
|
//
|
|
CHAR ConversionBuffer[(SIZE_FILE_IO_BUFFER + 1)*sizeof(TCHAR)];
|
|
|
|
TCHAR Buffer[SIZE_FILE_IO_BUFFER + 1];
|
|
FILE_HANDLE hFile;
|
|
int BufferOffset;
|
|
int CurrentColumn;
|
|
int CharsAvailable;
|
|
DWORD FileSizeDiv100;
|
|
DWORD FileOffset;
|
|
UINT LastPercentage;
|
|
#ifdef DEBUG
|
|
BOOL fValidateUngetChar;
|
|
#endif
|
|
} FILE_IO;
|
|
|
|
FILE_IO s_FileIo;
|
|
|
|
UINT g_FileErrorStringID;
|
|
|
|
UINT g_ImportFileVersion;
|
|
|
|
BOOL s_fTreatFileAsUnicode = TRUE;
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ImportWin31RegFile(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ImportNewerRegFile(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ParseHeader(
|
|
LPHKEY lphKey
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ParseValuename(
|
|
HKEY hKey
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ParseDefaultValue(
|
|
HKEY hKey
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseString(
|
|
LPTSTR lpString,
|
|
LPDWORD cbStringData
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexSequence(
|
|
LPBYTE lpHexData,
|
|
LPDWORD lpcbHexData
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexDword(
|
|
LPDWORD lpDword
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexByte(
|
|
LPBYTE lpByte
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexDigit(
|
|
LPBYTE lpDigit
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseEndOfLine(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
SkipWhitespace(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
SkipPastEndOfLine(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
GetChar(
|
|
PTCHAR lpChar
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
UngetChar(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
MatchChar(
|
|
TCHAR CharToMatch
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
IsWhitespace(
|
|
TCHAR Char
|
|
);
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
IsNewLine(
|
|
TCHAR Char
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutBranch(
|
|
HKEY hKey,
|
|
LPTSTR lpKeyName
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutLiteral(
|
|
LPCTSTR lpString
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutString(
|
|
LPCTSTR lpString
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutBinary(
|
|
CONST BYTE FAR* lpBuffer,
|
|
DWORD Type,
|
|
DWORD cbBytes
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutDword(
|
|
DWORD Dword,
|
|
BOOL fLeadingZeroes
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutChar(
|
|
TCHAR Char
|
|
);
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
FlushIoBuffer(
|
|
VOID
|
|
);
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* EditRegistryKey
|
|
*
|
|
* DESCRIPTION:
|
|
* Parses the pFullKeyName string and creates a handle to the registry key.
|
|
*
|
|
* PARAMETERS:
|
|
* lphKey, location to store handle to registry key.
|
|
* lpFullKeyName, string of form "HKEY_LOCAL_MACHINE\Subkey1\Subkey2".
|
|
* fCreate, TRUE if key should be created, else FALSE for open only.
|
|
* (returns), ERROR_SUCCESS, no errors occurred, phKey is valid,
|
|
* ERROR_CANTOPEN, registry access error of some form,
|
|
* ERROR_BADKEY, incorrectly formed pFullKeyName.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
DWORD
|
|
PASCAL
|
|
EditRegistryKey(
|
|
LPHKEY lphKey,
|
|
LPTSTR lpFullKeyName,
|
|
UINT uOperation
|
|
)
|
|
{
|
|
|
|
LPTSTR lpSubKeyName;
|
|
TCHAR PrevChar;
|
|
HKEY hRootKey;
|
|
UINT Counter;
|
|
DWORD Result;
|
|
|
|
if ((lpSubKeyName = (LPTSTR) STRCHR(lpFullKeyName, TEXT('\\'))) != NULL) {
|
|
|
|
PrevChar = *lpSubKeyName;
|
|
*lpSubKeyName++ = 0;
|
|
|
|
}
|
|
|
|
CHARUPPERSTRING(lpFullKeyName);
|
|
|
|
hRootKey = NULL;
|
|
|
|
for (Counter = 0; Counter < NUMBER_REGISTRY_ROOTS; Counter++) {
|
|
|
|
if (STRCMP(g_RegistryRoots[Counter].lpKeyName, lpFullKeyName) == 0) {
|
|
|
|
hRootKey = g_RegistryRoots[Counter].hKey;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hRootKey) {
|
|
|
|
Result = ERROR_CANTOPEN;
|
|
|
|
switch (uOperation) {
|
|
case ERK_CREATE:
|
|
if (RegCreateKey(hRootKey, lpSubKeyName, lphKey) == ERROR_SUCCESS)
|
|
Result = ERROR_SUCCESS;
|
|
break;
|
|
case ERK_OPEN:
|
|
//
|
|
// Used when exporting.
|
|
//
|
|
if(RegOpenKeyEx(hRootKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,lphKey) == ERROR_SUCCESS)
|
|
Result = ERROR_SUCCESS;
|
|
break;
|
|
case ERK_DELETE:
|
|
RegDeleteKeyRecursive(hRootKey, lpSubKeyName);
|
|
// asssume success... don't care if this fails
|
|
Result = ERROR_SUCCESS;
|
|
*lphKey = NULL;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
Result = ERROR_BADKEY;
|
|
|
|
if (lpSubKeyName != NULL) {
|
|
|
|
lpSubKeyName--;
|
|
*lpSubKeyName = PrevChar;
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ImportRegFileWorker
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* lpFileName, address of name of file to be imported.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
ImportRegFileWorker(
|
|
LPTSTR lpFileName
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
LPCTSTR lpHeader;
|
|
BOOL fNewRegistryFile;
|
|
#ifdef UNICODE
|
|
UINT Temp, i;
|
|
TCHAR StrToIntBuf[2];
|
|
LPCTSTR lp50Header;
|
|
#endif // UNICODE
|
|
DWORD cch;
|
|
TCHAR tchBuffer[MAX_PATH] = {0};
|
|
LPTSTR lpFilePart;
|
|
|
|
g_FileErrorStringID = IDS_IMPFILEERRSUCCESS;
|
|
|
|
// OPENREADFILE used to be OpenFile(), but there isn't any Unicode version
|
|
// of that API, so now it's CreateFile(). But OpenFile searched the path
|
|
// automatically, whereas CreateFile does not. Corel's 'Perfect Office v6'
|
|
// install app depends on the path being searched, so do it manually.
|
|
|
|
cch = SearchPath(NULL, // pointer to search path
|
|
lpFileName, // pointer to filename
|
|
NULL, // pointer to extension
|
|
ARRAYSIZE(tchBuffer), // size, in characters, of buffer
|
|
(TCHAR*)tchBuffer, // pointer to buffer for found filename
|
|
&lpFilePart); // pointer to pointer to file component);
|
|
|
|
if ((cch != 0) && (cch <= MAX_PATH) && OPENREADFILE((TCHAR*)tchBuffer, s_FileIo.hFile)) {
|
|
|
|
WORD wBOM;
|
|
DWORD NumberOfBytesRead;
|
|
|
|
s_FileIo.FileSizeDiv100 = GETFILESIZE(s_FileIo.hFile) / 100;
|
|
s_FileIo.FileOffset = 0;
|
|
s_FileIo.LastPercentage = 0;
|
|
|
|
//
|
|
// Read the first two bytes. If it's the Unicode byte order mark,
|
|
// set a flag so all the rest of the file will be interpreted
|
|
// as ANSI or Unicode text properly.
|
|
//
|
|
if (!READFILE(s_FileIo.hFile, &wBOM,
|
|
sizeof(wBOM), &NumberOfBytesRead)) {
|
|
|
|
g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
if (wBOM == s_UnicodeByteOrderMark)
|
|
s_fTreatFileAsUnicode = TRUE;
|
|
else {
|
|
s_fTreatFileAsUnicode = FALSE;
|
|
// We probably just read "RE" from "REGEDIT4". Back up the file
|
|
// position so the ANSI import routines get what they expect
|
|
SetFilePointer(s_FileIo.hFile, -2, NULL, FILE_CURRENT);
|
|
}
|
|
|
|
//
|
|
// The following will force GetChar to read in the first block of data.
|
|
//
|
|
|
|
// Unfortunately, in the Unicode version, it's not so clean.
|
|
// SIZE_FILE_IO_BUFFER is in bytes, but s_FileIo.BufferOffset is in chars,
|
|
// so we divide according to whether we're reading ANSI or Unicode.
|
|
|
|
s_FileIo.BufferOffset = SIZE_FILE_IO_BUFFER / (s_fTreatFileAsUnicode ? sizeof(WCHAR) : 1);
|
|
|
|
SkipWhitespace();
|
|
|
|
lpHeader = s_RegistryHeader;
|
|
g_ImportFileVersion = 0;
|
|
|
|
# if 0
|
|
Sit back, and I will tell ye a tale of woe.
|
|
|
|
Win95 and NT 4 shipped with regedit compiled ANSI. There are a couple
|
|
of registry types on NT (namely REG_EXPAND_SZ and REG_MULTI_SZ) that
|
|
weren't on Win95, and which regedit doesn't really understand. regedit
|
|
treats these registry types as hex binary streams.
|
|
|
|
You can probably see where this is going.
|
|
|
|
If you exported, say your user TEMP environment variable on NT 4
|
|
using regedit, you'd get something that looked like this:
|
|
|
|
REGEDIT4
|
|
|
|
[HKEY_CURRENT_USER\Environment]
|
|
"TEMP"=hex(2):25,53,59,53,54,45,4d,44,52,49,56,45,25,5c,53,48,54,65,6d,70,00
|
|
|
|
...a nice, null-terminated ANSI string. Nice, that is, until we decided
|
|
to compile regedit UNICODE for NT 5. A unicode regedit exports your
|
|
user TEMP variable like this:
|
|
|
|
REGEDIT4
|
|
|
|
[HKEY_CURRENT_USER\Environment]
|
|
"TEMP"=hex(2):25,00,53,00,59,00,53,00,54,00,45,00,4d,00,44,00,52,00,49,00,56,\
|
|
00,45,00,25,00,5c,00,53,00,48,00,54,00,65,00,6d,00,70,00,00,00
|
|
|
|
...mmmm. Unicode. Of course, a unicode regedit also expects anything
|
|
it imports to have all those interspersed zeroes, too. Otherwise,
|
|
it dumps garbage into your registry. All it takes is a -DUNICODE, and
|
|
regedit is suddenly incompatible with the thousdands of existing .reg
|
|
files out there.
|
|
|
|
So just bump the version in the header to REGEDIT5 and be done with
|
|
it, right? Wrong. The regedit on Win95 and NT 4 looks at the first
|
|
character after the string "REGEDIT" and compares it to the digit "4".
|
|
If that character is anything other than the digit "4", the parser
|
|
assumes it is looking at a Windows 3.1 file. Yep. There will only
|
|
ever be two formats, right? Just Win95 and Win3.1. That's all the
|
|
world needs.
|
|
|
|
So a completely new .reg file header had to be invented, so that the
|
|
older, brain damaged regedits of the world would simply regect the new,
|
|
unicodized .reg files outright. An NT 5 .reg file, exporting your user
|
|
TEMP variable, looks like this:
|
|
|
|
Windows Registry Editor Version 5.00
|
|
|
|
[HKEY_CURRENT_USER\Environment]
|
|
"TEMP"=hex(2):25,00,53,00,59,00,53,00,54,00,45,00,4d,00,44,00,52,00,49,00,56,\
|
|
00,45,00,25,00,5c,00,53,00,48,00,54,00,65,00,6d,00,70,00,00,00
|
|
|
|
The parser is still brain-dead, but it does bother to convert that 5.00
|
|
into a version number, so that future generations can bump it to 5.50 or
|
|
6.00, and the regedit 5.00 that shipped with NT 5.00 will properly reject
|
|
the files.
|
|
#endif // 0
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// Compare to the new .reg file header
|
|
//
|
|
lp50Header = s_WinNT50RegFileHeader;
|
|
while (*lp50Header != 0) {
|
|
|
|
if (MatchChar(*lp50Header))
|
|
lp50Header = CharNext(lp50Header);
|
|
|
|
else
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// If the above loop pushed lp50Header to its terminating null
|
|
// character, then the header matches.
|
|
//
|
|
if (0 == *lp50Header) {
|
|
|
|
SkipWhitespace();
|
|
//
|
|
// Now, decode the version number into a hex, _WIN32_WINNT
|
|
// style version number.
|
|
//
|
|
StrToIntBuf[1] = 0;
|
|
|
|
//
|
|
// Any number of digits can come before the decimal point
|
|
//
|
|
while (!MatchChar(TEXT('.'))) {
|
|
if (!GetChar(StrToIntBuf) || !IsCharAlphaNumeric(*StrToIntBuf)) {
|
|
g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// Temp = StrToInt(StrToIntBuf);
|
|
Temp = _tcstoul(StrToIntBuf, NULL, 10);
|
|
// Hex version number, so move left four bits.
|
|
g_ImportFileVersion <<= 4;
|
|
g_ImportFileVersion += Temp;
|
|
}
|
|
|
|
//
|
|
// Fixed at two digits after the decimal point
|
|
//
|
|
for (i = 0; i < 2; i++) {
|
|
if (!GetChar(StrToIntBuf) || !IsCharAlphaNumeric(*StrToIntBuf)) {
|
|
g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// Temp = StrToInt(StrToIntBuf);
|
|
Temp = _tcstoul(StrToIntBuf, NULL, 10);
|
|
// Hex version number, so move left four bits.
|
|
g_ImportFileVersion <<= 4;
|
|
g_ImportFileVersion += Temp;
|
|
}
|
|
|
|
//
|
|
// For NT 5, reject any version number that isn't
|
|
// 5. This can be expanded into a switch statement
|
|
// when the version number is bumped later.
|
|
//
|
|
if (0x0500 != g_ImportFileVersion) {
|
|
g_FileErrorStringID = IDS_IMPFILEERRVERBAD;
|
|
goto exit_gracefully;
|
|
}
|
|
else {
|
|
SkipWhitespace();
|
|
ImportNewerRegFile();
|
|
}
|
|
|
|
} // if (0 == *lp50Header)
|
|
//
|
|
// It doesn't use the new .reg file header, so
|
|
// it's not an NT 5.0+ registry file, so use the brain dead
|
|
// older algorithm to see if it's a valid older registry file
|
|
//
|
|
else {
|
|
#endif // UNICODE
|
|
|
|
while (*lpHeader != 0) {
|
|
|
|
if (MatchChar(*lpHeader))
|
|
lpHeader = CharNext(lpHeader);
|
|
|
|
else
|
|
break;
|
|
|
|
}
|
|
|
|
if (*lpHeader == 0) {
|
|
|
|
//
|
|
// Win95's and NT 4's regedit shipped with this line
|
|
// of code. It is the cause of all of the suffering above.
|
|
// Notice the incorrect assumption: "If the very next
|
|
// character isn't a '4', then we must be reading
|
|
// a Windows 3.1 registry file!" Of course there won't
|
|
// be a version 5 of regedit. Version 4 was perfect!
|
|
//
|
|
fNewRegistryFile = MatchChar(TEXT('4'));
|
|
|
|
SkipWhitespace();
|
|
|
|
if (GetChar(&Char) && IsNewLine(Char)) {
|
|
|
|
if (fNewRegistryFile) {
|
|
g_ImportFileVersion = 0x0400;
|
|
ImportNewerRegFile();
|
|
}
|
|
else {
|
|
g_ImportFileVersion = 0x0310;
|
|
ImportWin31RegFile();
|
|
}
|
|
|
|
}
|
|
|
|
} // if (*lpHeader == 0)
|
|
|
|
else
|
|
g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
|
|
#ifdef UNICODE
|
|
}
|
|
#endif // UNICODE
|
|
|
|
} // if (OPENREADFILE...
|
|
|
|
else {
|
|
{ TCHAR buff[250];
|
|
wsprintf(buff, TEXT("REGEDIT: CreateFile failed, GetLastError() = %d\n"), GetLastError());
|
|
OutputDebugString(buff);
|
|
}
|
|
s_FileIo.hFile = NULL;
|
|
g_FileErrorStringID = IDS_IMPFILEERRFILEOPEN;
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if (s_FileIo.hFile) {
|
|
CLOSEFILE(s_FileIo.hFile);
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ImportWin31RegFile
|
|
*
|
|
* DESCRIPTION:
|
|
* Imports the contents of a Windows 3.1 style registry file into the
|
|
* registry.
|
|
*
|
|
* We scan over the file looking for lines of the following type:
|
|
* HKEY_CLASSES_ROOT\keyname = value_data
|
|
* HKEY_CLASSES_ROOT\keyname =value_data
|
|
* HKEY_CLASSES_ROOT\keyname value_data
|
|
* HKEY_CLASSES_ROOT\keyname (null value data)
|
|
*
|
|
* In all cases, any number of spaces may follow 'keyname'. Although we
|
|
* only document the first syntax, the Windows 3.1 Regedit handled all of
|
|
* these formats as valid, so this version will as well (fortunately, it
|
|
* doesn't make the parsing any more complex!).
|
|
*
|
|
* Note, we also support replacing HKEY_CLASSES_ROOT with \.classes above
|
|
* which must come from some early releases of Windows.
|
|
*
|
|
* PARAMETERS:
|
|
* (none).
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ImportWin31RegFile(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
HKEY hKey;
|
|
TCHAR Char;
|
|
BOOL fSuccess;
|
|
LPCTSTR lpClassesRoot;
|
|
TCHAR KeyName[MAXKEYNAME];
|
|
UINT Index;
|
|
|
|
//
|
|
// Keep an open handle to the classes root. We may prevent some
|
|
// unneccessary flushing.
|
|
//
|
|
if(RegOpenKeyEx(HKEY_CLASSES_ROOT,NULL,0,KEY_SET_VALUE,&hKey) != ERROR_SUCCESS) {
|
|
|
|
g_FileErrorStringID = IDS_IMPFILEERRREGOPEN;
|
|
return;
|
|
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Check for the end of file condition.
|
|
//
|
|
|
|
if (!GetChar(&Char))
|
|
break;
|
|
|
|
UngetChar(); // Not efficient, but works for now.
|
|
|
|
//
|
|
// Match the beginning of the line against one of the two aliases for
|
|
// HKEY_CLASSES_ROOT.
|
|
//
|
|
|
|
if (MatchChar(TEXT('\\')))
|
|
lpClassesRoot = s_OldWin31RegFileRoot;
|
|
|
|
else
|
|
lpClassesRoot = g_RegistryRoots[INDEX_HKEY_CLASSES_ROOT].lpKeyName;
|
|
|
|
fSuccess = TRUE;
|
|
|
|
while (*lpClassesRoot != 0) {
|
|
|
|
if (!MatchChar(*lpClassesRoot++)) {
|
|
|
|
fSuccess = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that we have a backslash seperating one of the aliases
|
|
// from the keyname.
|
|
//
|
|
|
|
if (fSuccess)
|
|
fSuccess = MatchChar(TEXT('\\'));
|
|
|
|
if (fSuccess) {
|
|
|
|
//
|
|
// We've found one of the valid aliases, so read in the keyname.
|
|
//
|
|
|
|
// fSuccess = TRUE; // Must be TRUE if we're in this block
|
|
Index = 0;
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (Char == TEXT(' ') || IsNewLine(Char))
|
|
break;
|
|
|
|
//
|
|
// Make sure that the keyname buffer doesn't overflow. We must
|
|
// leave room for a terminating null.
|
|
//
|
|
|
|
if (Index >= (sizeof(KeyName)/sizeof(TCHAR)) - 1) {
|
|
|
|
fSuccess = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
KeyName[Index++] = Char;
|
|
|
|
}
|
|
|
|
if (fSuccess) {
|
|
|
|
KeyName[Index] = 0;
|
|
|
|
//
|
|
// Now see if we have a value to assign to this keyname.
|
|
//
|
|
|
|
SkipWhitespace();
|
|
|
|
if (MatchChar(TEXT('=')))
|
|
MatchChar(TEXT(' '));
|
|
|
|
// fSuccess = TRUE; // Must be TRUE if we're in this block
|
|
Index = 0;
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (IsNewLine(Char))
|
|
break;
|
|
|
|
//
|
|
// Make sure that the value data buffer doesn't overflow.
|
|
// Because this is always string data, we must leave room
|
|
// for a terminating null.
|
|
//
|
|
|
|
if (Index >= MAXDATA_LENGTH - 1) {
|
|
|
|
fSuccess = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
((PTSTR)g_ValueDataBuffer)[Index++] = Char;
|
|
|
|
}
|
|
|
|
if (fSuccess) {
|
|
|
|
((PTSTR)g_ValueDataBuffer)[Index] = 0;
|
|
|
|
if (RegSetValue(hKey, KeyName, REG_SZ, (LPCTSTR)g_ValueDataBuffer,
|
|
Index*sizeof(TCHAR)) != ERROR_SUCCESS)
|
|
g_FileErrorStringID = IDS_IMPFILEERRREGSET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Somewhere along the line, we had a parsing error, so resynchronize
|
|
// on the next line.
|
|
//
|
|
|
|
if (!fSuccess)
|
|
SkipPastEndOfLine();
|
|
|
|
}
|
|
|
|
RegFlushKey(hKey);
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ImportNewerRegFile
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ImportNewerRegFile(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
HKEY hLocalMachineKey;
|
|
HKEY hUsersKey;
|
|
HKEY hKey;
|
|
TCHAR Char;
|
|
|
|
#ifdef WINNT
|
|
hLocalMachineKey = NULL;
|
|
hUsersKey = NULL;
|
|
#else
|
|
//
|
|
// Keep open handles for the predefined roots to prevent the registry
|
|
// library from flushing after every single RegOpenKey/RegCloseKey
|
|
// operation.
|
|
//
|
|
|
|
RegOpenKey(HKEY_LOCAL_MACHINE, NULL, &hLocalMachineKey);
|
|
RegOpenKey(HKEY_USERS, NULL, &hUsersKey);
|
|
|
|
#ifdef DEBUG
|
|
if (hLocalMachineKey == NULL)
|
|
DbgPrintf(("Unable to open HKEY_LOCAL_MACHINE\n\r"));
|
|
if (hUsersKey == NULL)
|
|
DbgPrintf(("Unable to open HKEY_USERS\n\r"));
|
|
#endif
|
|
#endif
|
|
|
|
hKey = NULL;
|
|
|
|
while (TRUE) {
|
|
|
|
SkipWhitespace();
|
|
|
|
//
|
|
// Check for the end of file condition.
|
|
//
|
|
|
|
if (!GetChar(&Char))
|
|
break;
|
|
|
|
switch (Char) {
|
|
|
|
case TEXT('['):
|
|
//
|
|
// If a registry key is currently open, we must close it first.
|
|
// If ParseHeader happens to fail (for example, no closing
|
|
// bracket), then hKey will be NULL and any values that we
|
|
// parse must be ignored.
|
|
//
|
|
|
|
if (hKey != NULL) {
|
|
|
|
RegCloseKey(hKey);
|
|
hKey = NULL;
|
|
|
|
}
|
|
|
|
ParseHeader(&hKey);
|
|
|
|
break;
|
|
|
|
case TEXT('"'):
|
|
//
|
|
// As noted above, if we don't have an open registry key, then
|
|
// just skip the line.
|
|
//
|
|
|
|
if (hKey != NULL)
|
|
ParseValuename(hKey);
|
|
|
|
else
|
|
SkipPastEndOfLine();
|
|
|
|
break;
|
|
|
|
case TEXT('@'):
|
|
//
|
|
//
|
|
//
|
|
|
|
if (hKey != NULL)
|
|
ParseDefaultValue(hKey);
|
|
|
|
else
|
|
SkipPastEndOfLine();
|
|
|
|
break;
|
|
|
|
case TEXT(';'):
|
|
//
|
|
// This line is a comment so just dump the rest of it.
|
|
//
|
|
|
|
SkipPastEndOfLine();
|
|
|
|
break;
|
|
|
|
default:
|
|
if (IsNewLine(Char))
|
|
break;
|
|
|
|
SkipPastEndOfLine();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
if (hUsersKey != NULL)
|
|
RegCloseKey(hUsersKey);
|
|
|
|
if (hLocalMachineKey != NULL)
|
|
RegCloseKey(hLocalMachineKey);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseHeader
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#define SIZE_FULL_KEYNAME (MAXKEYNAME + 40)
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ParseHeader(
|
|
LPHKEY lphKey
|
|
)
|
|
{
|
|
|
|
TCHAR FullKeyName[SIZE_FULL_KEYNAME];
|
|
int CurrentIndex;
|
|
int LastRightBracketIndex;
|
|
TCHAR Char;
|
|
UINT uOperation = ERK_CREATE;
|
|
|
|
CurrentIndex = 0;
|
|
LastRightBracketIndex = -1;
|
|
|
|
if (!GetChar(&Char))
|
|
return;
|
|
|
|
if (Char == TEXT('-')) {
|
|
if (!GetChar(&Char))
|
|
return;
|
|
uOperation = ERK_DELETE;
|
|
}
|
|
|
|
do {
|
|
|
|
if (IsNewLine(Char))
|
|
break;
|
|
|
|
if (Char == TEXT(']'))
|
|
LastRightBracketIndex = CurrentIndex;
|
|
|
|
FullKeyName[CurrentIndex++] = Char;
|
|
|
|
if (CurrentIndex == SIZE_FULL_KEYNAME) {
|
|
|
|
do {
|
|
|
|
if (Char == TEXT(']'))
|
|
LastRightBracketIndex = -1;
|
|
|
|
if (IsNewLine(Char))
|
|
break;
|
|
|
|
} while (GetChar(&Char));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (GetChar(&Char));
|
|
|
|
if (LastRightBracketIndex != -1) {
|
|
|
|
FullKeyName[LastRightBracketIndex] = 0;
|
|
|
|
switch (EditRegistryKey(lphKey, FullKeyName, uOperation)) {
|
|
|
|
case ERROR_CANTOPEN:
|
|
g_FileErrorStringID = IDS_IMPFILEERRREGOPEN;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseValuename
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ParseValuename(
|
|
HKEY hKey
|
|
)
|
|
{
|
|
|
|
DWORD Type;
|
|
DWORD dwResult = 0;
|
|
TCHAR ValueName[MAXVALUENAME_LENGTH];
|
|
DWORD cbData;
|
|
LPCTSTR lpPrefix;
|
|
|
|
cbData = sizeof(ValueName);
|
|
|
|
if (!ParseString(ValueName, &cbData))
|
|
goto ParseError;
|
|
|
|
SkipWhitespace();
|
|
|
|
if (!MatchChar(TEXT('=')))
|
|
goto ParseError;
|
|
|
|
SkipWhitespace();
|
|
|
|
//
|
|
// REG_SZ.
|
|
//
|
|
// "ValueName" = "string of text"
|
|
//
|
|
|
|
if (MatchChar(TEXT('"'))) {
|
|
|
|
// BUGBUG: Line continuations for strings?
|
|
|
|
cbData = MAXDATA_LENGTH;
|
|
|
|
if (!ParseString((PTSTR)g_ValueDataBuffer, &cbData) || !ParseEndOfLine())
|
|
goto ParseError;
|
|
|
|
Type = REG_SZ;
|
|
|
|
}
|
|
|
|
//
|
|
// REG_DWORD.
|
|
//
|
|
// "ValueName" = dword: 12345678
|
|
//
|
|
|
|
else if (MatchChar(s_DwordPrefix[0])) {
|
|
|
|
lpPrefix = &s_DwordPrefix[1];
|
|
|
|
while (*lpPrefix != 0)
|
|
if (!MatchChar(*lpPrefix++))
|
|
goto ParseError;
|
|
|
|
SkipWhitespace();
|
|
|
|
if (!ParseHexDword((LPDWORD) g_ValueDataBuffer) || !ParseEndOfLine())
|
|
goto ParseError;
|
|
|
|
Type = REG_DWORD;
|
|
cbData = sizeof(DWORD);
|
|
|
|
}
|
|
else if (MatchChar('-')) {
|
|
|
|
if (!ParseEndOfLine())
|
|
goto ParseError;
|
|
RegDeleteValue(hKey, ValueName);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// REG_BINARY and other.
|
|
//
|
|
// "ValueName" = hex: 00 , 11 , 22
|
|
// "ValueName" = hex(12345678): 00, 11, 22
|
|
//
|
|
|
|
else {
|
|
|
|
lpPrefix = s_HexPrefix;
|
|
|
|
while (*lpPrefix != 0)
|
|
if (!MatchChar(*lpPrefix++))
|
|
goto ParseError;
|
|
|
|
//
|
|
// Check if this is a type of registry data that we don't directly
|
|
// support. If so, then it's just a dump of hex data of the specified
|
|
// type.
|
|
//
|
|
|
|
if (MatchChar(TEXT('('))) {
|
|
|
|
if (!ParseHexDword(&Type) || !MatchChar(TEXT(')')))
|
|
goto ParseError;
|
|
|
|
}
|
|
|
|
else
|
|
Type = REG_BINARY;
|
|
|
|
if (!MatchChar(TEXT(':')) || !ParseHexSequence(g_ValueDataBuffer, &cbData) ||
|
|
!ParseEndOfLine())
|
|
goto ParseError;
|
|
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// If we're compiled UNICODE and we're reading an older, ANSI .reg
|
|
// file, we have to write all of the data to the registry using
|
|
// RegSetValueExA, because it was read from the registry using
|
|
// RegQueryValueExA.
|
|
//
|
|
if ((g_ImportFileVersion < 0x0500) && ((REG_EXPAND_SZ == Type) || (REG_MULTI_SZ == Type))) {
|
|
CHAR AnsiValueName[MAXVALUENAME_LENGTH];
|
|
|
|
//
|
|
// It's much easier to convert the value name to ANSI
|
|
// and call RegSetValueExA than to try to convert
|
|
// a REG_MULTI_SZ to Unicode before calling RegSetValueExW.
|
|
// We don't lose anything because this is coming from a
|
|
// downlevel .reg file that could only contain ANSI characters
|
|
// to begin with.
|
|
//
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
ValueName,
|
|
-1,
|
|
AnsiValueName,
|
|
MAXVALUENAME_LENGTH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (RegSetValueExA(
|
|
hKey,
|
|
AnsiValueName,
|
|
0,
|
|
Type,
|
|
g_ValueDataBuffer,
|
|
cbData)
|
|
!= ERROR_SUCCESS)
|
|
g_FileErrorStringID = IDS_IMPFILEERRREGSET;
|
|
}
|
|
else {
|
|
#endif // UNICODE
|
|
dwResult = RegSetValueEx(hKey, ValueName, 0, Type, g_ValueDataBuffer, cbData);
|
|
if ( dwResult != ERROR_SUCCESS)
|
|
{
|
|
g_FileErrorStringID = IDS_IMPFILEERRREGSET;
|
|
SetLastError( dwResult );
|
|
}
|
|
#ifdef UNICODE
|
|
}
|
|
#endif // UNICODE
|
|
|
|
return;
|
|
|
|
ParseError:
|
|
SkipPastEndOfLine();
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseDefaultValue
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
ParseDefaultValue(
|
|
HKEY hKey
|
|
)
|
|
{
|
|
|
|
BOOL fSuccess;
|
|
DWORD cbData;
|
|
|
|
fSuccess = FALSE;
|
|
|
|
SkipWhitespace();
|
|
|
|
if (MatchChar(TEXT('='))) {
|
|
|
|
SkipWhitespace();
|
|
|
|
if (MatchChar(TEXT('"'))) {
|
|
|
|
// BUGBUG: Line continuations for strings?
|
|
|
|
cbData = MAXDATA_LENGTH;
|
|
|
|
if (ParseString((PTSTR)g_ValueDataBuffer, &cbData) && ParseEndOfLine()) {
|
|
|
|
if (RegSetValue(hKey, NULL, REG_SZ, (LPCTSTR)g_ValueDataBuffer,
|
|
cbData) != ERROR_SUCCESS)
|
|
g_FileErrorStringID = IDS_IMPFILEERRREGSET;
|
|
|
|
fSuccess = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!fSuccess)
|
|
SkipPastEndOfLine();
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseString
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseString(
|
|
LPTSTR lpString,
|
|
LPDWORD lpcbStringData
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
DWORD cbMaxStringData;
|
|
DWORD cbStringData;
|
|
|
|
cbMaxStringData = *lpcbStringData;
|
|
cbStringData = 1; // Account for the null terminator
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (cbStringData >= cbMaxStringData)
|
|
return FALSE;
|
|
|
|
switch (Char) {
|
|
|
|
case TEXT('\\'):
|
|
if (!GetChar(&Char))
|
|
return FALSE;
|
|
|
|
switch (Char) {
|
|
|
|
case TEXT('\\'):
|
|
*lpString++ = TEXT('\\');
|
|
break;
|
|
|
|
case TEXT('"'):
|
|
*lpString++ = TEXT('"');
|
|
break;
|
|
|
|
default:
|
|
DbgPrintf(("ParseString: Invalid escape sequence"));
|
|
return FALSE;
|
|
|
|
}
|
|
break;
|
|
|
|
case TEXT('"'):
|
|
*lpString = 0;
|
|
*lpcbStringData = cbStringData * sizeof(TCHAR);
|
|
return TRUE;
|
|
|
|
default:
|
|
if (IsNewLine(Char))
|
|
return FALSE;
|
|
|
|
*lpString++ = Char;
|
|
break;
|
|
|
|
}
|
|
|
|
cbStringData++;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseHexSequence
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexSequence(
|
|
LPBYTE lpHexData,
|
|
LPDWORD lpcbHexData
|
|
)
|
|
{
|
|
|
|
DWORD cbHexData;
|
|
|
|
cbHexData = 0;
|
|
|
|
do {
|
|
|
|
if (cbHexData >= MAXDATA_LENGTH)
|
|
return FALSE;
|
|
|
|
SkipWhitespace();
|
|
|
|
if (MatchChar(TEXT('\\')) && !ParseEndOfLine())
|
|
return FALSE;
|
|
|
|
SkipWhitespace();
|
|
|
|
if (!ParseHexByte(lpHexData++))
|
|
break;
|
|
|
|
cbHexData++;
|
|
|
|
SkipWhitespace();
|
|
|
|
} while (MatchChar(TEXT(',')));
|
|
|
|
*lpcbHexData = cbHexData;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseHexDword
|
|
*
|
|
* DESCRIPTION:
|
|
* Parses a one dword hexadecimal string from the registry file stream and
|
|
* converts it to a binary number. A maximum of eight hex digits will be
|
|
* parsed from the stream.
|
|
*
|
|
* PARAMETERS:
|
|
* lpByte, location to store binary number.
|
|
* (returns), TRUE if a hexadecimal dword was parsed, else FALSE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexDword(
|
|
LPDWORD lpDword
|
|
)
|
|
{
|
|
|
|
UINT CountDigits;
|
|
DWORD Dword;
|
|
BYTE Byte;
|
|
|
|
Dword = 0;
|
|
CountDigits = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
if (!ParseHexDigit(&Byte))
|
|
break;
|
|
|
|
Dword = (Dword << 4) + (DWORD) Byte;
|
|
|
|
if (++CountDigits == 8)
|
|
break;
|
|
|
|
}
|
|
|
|
*lpDword = Dword;
|
|
|
|
return CountDigits != 0;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseHexByte
|
|
*
|
|
* DESCRIPTION:
|
|
* Parses a one byte hexadecimal string from the registry file stream and
|
|
* converts it to a binary number.
|
|
*
|
|
* PARAMETERS:
|
|
* lpByte, location to store binary number.
|
|
* (returns), TRUE if a hexadecimal byte was parsed, else FALSE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexByte(
|
|
LPBYTE lpByte
|
|
)
|
|
{
|
|
|
|
BYTE SecondDigit;
|
|
|
|
if (ParseHexDigit(lpByte)) {
|
|
|
|
if (ParseHexDigit(&SecondDigit))
|
|
*lpByte = (BYTE) ((*lpByte << 4) | SecondDigit);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseHexDigit
|
|
*
|
|
* DESCRIPTION:
|
|
* Parses a hexadecimal character from the registry file stream and converts
|
|
* it to a binary number.
|
|
*
|
|
* PARAMETERS:
|
|
* lpDigit, location to store binary number.
|
|
* (returns), TRUE if a hexadecimal digit was parsed, else FALSE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseHexDigit(
|
|
LPBYTE lpDigit
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
BYTE Digit;
|
|
|
|
if (GetChar(&Char)) {
|
|
|
|
if (Char >= TEXT('0') && Char <= TEXT('9'))
|
|
Digit = (BYTE) (Char - TEXT('0'));
|
|
|
|
else if (Char >= TEXT('a') && Char <= TEXT('f'))
|
|
Digit = (BYTE) (Char - TEXT('a') + 10);
|
|
|
|
else if (Char >= TEXT('A') && Char <= TEXT('F'))
|
|
Digit = (BYTE) (Char - TEXT('A') + 10);
|
|
|
|
else {
|
|
|
|
UngetChar();
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
*lpDigit = Digit;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ParseEndOfLine
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
ParseEndOfLine(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
BOOL fComment;
|
|
BOOL fFoundOneEndOfLine;
|
|
|
|
fComment = FALSE;
|
|
fFoundOneEndOfLine = FALSE;
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (IsWhitespace(Char))
|
|
continue;
|
|
|
|
if (IsNewLine(Char)) {
|
|
|
|
fComment = FALSE;
|
|
fFoundOneEndOfLine = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Like .INIs and .INFs, comments begin with a semicolon character.
|
|
//
|
|
|
|
else if (Char == TEXT(';'))
|
|
fComment = TRUE;
|
|
|
|
else if (!fComment) {
|
|
|
|
UngetChar();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fFoundOneEndOfLine;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SkipWhitespace
|
|
*
|
|
* DESCRIPTION:
|
|
* Advances the registry file pointer to the first character past any
|
|
* detected whitespace.
|
|
*
|
|
* PARAMETERS:
|
|
* (none).
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
SkipWhitespace(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (!IsWhitespace(Char)) {
|
|
|
|
UngetChar();
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SkipPastEndOfLine
|
|
*
|
|
* DESCRIPTION:
|
|
* Advances the registry file pointer to the first character past the first
|
|
* detected new line character.
|
|
*
|
|
* PARAMETERS:
|
|
* (none).
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
SkipPastEndOfLine(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (IsNewLine(Char))
|
|
break;
|
|
|
|
}
|
|
|
|
while (GetChar(&Char)) {
|
|
|
|
if (!IsNewLine(Char)) {
|
|
|
|
UngetChar();
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* GetChar
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
GetChar(
|
|
PTCHAR lpChar
|
|
)
|
|
{
|
|
|
|
FILE_NUMBYTES NumberOfBytesRead;
|
|
UINT NewPercentage;
|
|
|
|
// If we're at the end of the buffer, read some more.
|
|
// SIZE_FILE_IO_BUFFER is in bytes, but s_FileIo.BufferOffset is in chars,
|
|
// so we multiply according to whether we're reading ANSI or Unicode.
|
|
if ((s_FileIo.BufferOffset * (s_fTreatFileAsUnicode ? sizeof(WCHAR) : 1)) == SIZE_FILE_IO_BUFFER) {
|
|
|
|
if (TRUE == s_fTreatFileAsUnicode) {
|
|
if (!READFILE(s_FileIo.hFile, s_FileIo.Buffer,
|
|
SIZE_FILE_IO_BUFFER, &NumberOfBytesRead)) {
|
|
|
|
g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
|
|
return FALSE;
|
|
}
|
|
|
|
s_FileIo.CharsAvailable = ((int) NumberOfBytesRead / 2);
|
|
}
|
|
else {
|
|
if (!READFILE(s_FileIo.hFile, s_FileIo.ConversionBuffer,
|
|
SIZE_FILE_IO_BUFFER, &NumberOfBytesRead)) {
|
|
|
|
g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
|
|
return FALSE;
|
|
}
|
|
|
|
{
|
|
int i;
|
|
|
|
#ifdef UNICODE
|
|
i = MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
s_FileIo.ConversionBuffer,
|
|
NumberOfBytesRead,
|
|
s_FileIo.Buffer,
|
|
SIZE_FILE_IO_BUFFER
|
|
);
|
|
#else
|
|
lstrcpyn(s_FileIo.Buffer, s_FileIo.ConversionBuffer, NumberOfBytesRead + 1);
|
|
i = lstrlen(s_FileIo.Buffer);
|
|
#endif //UNICODE
|
|
|
|
s_FileIo.CharsAvailable = i;
|
|
}
|
|
}
|
|
|
|
s_FileIo.BufferOffset = 0;
|
|
s_FileIo.FileOffset += NumberOfBytesRead;
|
|
|
|
if (s_FileIo.FileSizeDiv100 != 0) {
|
|
|
|
NewPercentage = ((UINT) (s_FileIo.FileOffset /
|
|
s_FileIo.FileSizeDiv100));
|
|
|
|
if (NewPercentage > 100)
|
|
NewPercentage = 100;
|
|
|
|
}
|
|
|
|
else
|
|
NewPercentage = 100;
|
|
|
|
if (s_FileIo.LastPercentage != NewPercentage) {
|
|
|
|
s_FileIo.LastPercentage = NewPercentage;
|
|
// no UI
|
|
// ImportRegFileUICallback(NewPercentage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (s_FileIo.BufferOffset >= s_FileIo.CharsAvailable)
|
|
return FALSE;
|
|
|
|
*lpChar = s_FileIo.Buffer[s_FileIo.BufferOffset++];
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* UngetChar
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
UngetChar(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
if (s_FileIo.fValidateUngetChar)
|
|
DbgPrintf(("REGEDIT ERROR: Too many UngetChar's called!\n\r"));
|
|
#endif
|
|
|
|
s_FileIo.BufferOffset--;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* MatchChar
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
MatchChar(
|
|
TCHAR CharToMatch
|
|
)
|
|
{
|
|
|
|
BOOL fMatch;
|
|
TCHAR NextChar;
|
|
|
|
fMatch = FALSE;
|
|
|
|
if (GetChar(&NextChar)) {
|
|
|
|
if (CharToMatch == NextChar)
|
|
fMatch = TRUE;
|
|
|
|
else
|
|
UngetChar();
|
|
|
|
}
|
|
|
|
return fMatch;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* IsWhitespace
|
|
*
|
|
* DESCRIPTION:
|
|
* Checks if the given character is whitespace.
|
|
*
|
|
* PARAMETERS:
|
|
* Char, character to check.
|
|
* (returns), TRUE if character is whitespace, else FALSE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
IsWhitespace(
|
|
TCHAR Char
|
|
)
|
|
{
|
|
|
|
return Char == TEXT(' ') || Char == TEXT('\t');
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* IsNewLine
|
|
*
|
|
* DESCRIPTION:
|
|
* Checks if the given character is a new line character.
|
|
*
|
|
* PARAMETERS:
|
|
* Char, character to check.
|
|
* (returns), TRUE if character is a new line, else FALSE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
NEAR PASCAL
|
|
IsNewLine(
|
|
TCHAR Char
|
|
)
|
|
{
|
|
|
|
return Char == TEXT('\n') || Char == TEXT('\r');
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ExportWinNT50RegFile
|
|
*
|
|
* DESCRIPTION:
|
|
* Exports an NT 5.0, unicode registry file. Use this export function
|
|
* for all future .reg file writing.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
VOID
|
|
PASCAL
|
|
ExportWinNT50RegFile(
|
|
LPTSTR lpFileName,
|
|
LPTSTR lpSelectedPath
|
|
)
|
|
{
|
|
|
|
HKEY hKey;
|
|
TCHAR SelectedPath[SIZE_SELECTED_PATH];
|
|
|
|
g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
|
|
|
|
if (lpSelectedPath != NULL && EditRegistryKey(&hKey, lpSelectedPath,
|
|
ERK_OPEN) != ERROR_SUCCESS) {
|
|
|
|
g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
|
|
return;
|
|
|
|
}
|
|
|
|
if (OPENWRITEFILE(lpFileName, s_FileIo.hFile)) {
|
|
|
|
DWORD dwNumberOfBytesWritten;
|
|
|
|
s_FileIo.BufferOffset = 0;
|
|
s_FileIo.CurrentColumn = 0;
|
|
|
|
WRITEFILE(s_FileIo.hFile, &s_UnicodeByteOrderMark, sizeof(s_UnicodeByteOrderMark), &dwNumberOfBytesWritten);
|
|
|
|
PutLiteral(s_WinNT50RegFileHeader);
|
|
PutLiteral(TEXT(" "));
|
|
PutLiteral(s_WinNT50RegFileVersion);
|
|
PutLiteral(TEXT("\n\n"));
|
|
|
|
if (lpSelectedPath != NULL) {
|
|
|
|
STRCPY(SelectedPath, lpSelectedPath);
|
|
PutBranch(hKey, SelectedPath);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
STRCPY(SelectedPath,
|
|
g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
|
|
PutBranch(HKEY_LOCAL_MACHINE, SelectedPath);
|
|
|
|
STRCPY(SelectedPath,
|
|
g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
|
|
PutBranch(HKEY_USERS, SelectedPath);
|
|
|
|
}
|
|
|
|
FlushIoBuffer();
|
|
|
|
CLOSEFILE(s_FileIo.hFile);
|
|
|
|
}
|
|
|
|
else
|
|
g_FileErrorStringID = IDS_EXPFILEERRFILEOPEN;
|
|
|
|
if (lpSelectedPath != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* ExportWin40RegFile
|
|
*
|
|
* DESCRIPTION:
|
|
* This function is only kept around to export old, ANSI, regedit 4 .reg
|
|
* files. Don't touch it except to fix bugs. Meddling with this code
|
|
* path will result in .reg files that can't be read by older verions
|
|
* of regedit, which is the whole reason this code path is here. Meddle
|
|
* with ExportWinNT50RegFile if you want to break backwards compatibility.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
ExportWin40RegFile(
|
|
LPTSTR lpFileName,
|
|
LPTSTR lpSelectedPath
|
|
)
|
|
{
|
|
|
|
HKEY hKey;
|
|
TCHAR SelectedPath[SIZE_SELECTED_PATH];
|
|
|
|
g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
|
|
|
|
if (lpSelectedPath != NULL && EditRegistryKey(&hKey, lpSelectedPath,
|
|
ERK_OPEN) != ERROR_SUCCESS) {
|
|
|
|
g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
|
|
return;
|
|
|
|
}
|
|
|
|
if (OPENWRITEFILE(lpFileName, s_FileIo.hFile)) {
|
|
|
|
s_FileIo.BufferOffset = 0;
|
|
s_FileIo.CurrentColumn = 0;
|
|
|
|
PutLiteral(s_Win40RegFileHeader);
|
|
|
|
if (lpSelectedPath != NULL) {
|
|
|
|
STRCPY(SelectedPath, lpSelectedPath);
|
|
PutBranch(hKey, SelectedPath);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
STRCPY(SelectedPath,
|
|
g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
|
|
PutBranch(HKEY_LOCAL_MACHINE, SelectedPath);
|
|
|
|
STRCPY(SelectedPath,
|
|
g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
|
|
PutBranch(HKEY_USERS, SelectedPath);
|
|
|
|
}
|
|
|
|
FlushIoBuffer();
|
|
|
|
CLOSEFILE(s_FileIo.hFile);
|
|
|
|
}
|
|
|
|
else
|
|
g_FileErrorStringID = IDS_EXPFILEERRFILEOPEN;
|
|
|
|
if (lpSelectedPath != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* PutBranch
|
|
*
|
|
* DESCRIPTION:
|
|
* Writes out all of the value names and their data and recursively calls
|
|
* this routine for all of the key's subkeys to the registry file stream.
|
|
*
|
|
* PARAMETERS:
|
|
* hKey, registry key to write to file.
|
|
* lpFullKeyName, string that gives the full path, including the root key
|
|
* name, of the hKey.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutBranch(
|
|
HKEY hKey,
|
|
LPTSTR lpFullKeyName
|
|
)
|
|
{
|
|
|
|
LONG RegError;
|
|
DWORD EnumIndex;
|
|
DWORD cbValueName;
|
|
DWORD cbValueData;
|
|
DWORD Type;
|
|
LPTSTR lpSubKeyName;
|
|
int MaximumSubKeyLength;
|
|
HKEY hSubKey;
|
|
|
|
//
|
|
// Write out the section header.
|
|
//
|
|
|
|
PutChar(TEXT('['));
|
|
PutLiteral(lpFullKeyName);
|
|
PutLiteral(TEXT("]\n"));
|
|
|
|
//
|
|
// Write out all of the value names and their data.
|
|
//
|
|
|
|
EnumIndex = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
cbValueName = sizeof(g_ValueNameBuffer);
|
|
cbValueData = MAXDATA_LENGTH;
|
|
|
|
if ((RegError = RegEnumValue(hKey, EnumIndex++, g_ValueNameBuffer,
|
|
&cbValueName, NULL, &Type, g_ValueDataBuffer, &cbValueData))
|
|
!= ERROR_SUCCESS)
|
|
break;
|
|
|
|
//
|
|
// If cbValueName is zero, then this is the default value of
|
|
// the key, or the Windows 3.1 compatible key value.
|
|
//
|
|
|
|
if (cbValueName)
|
|
PutString(g_ValueNameBuffer);
|
|
|
|
else
|
|
PutChar(TEXT('@'));
|
|
|
|
PutChar(TEXT('='));
|
|
|
|
switch (Type) {
|
|
|
|
case REG_SZ:
|
|
PutString((LPTSTR) g_ValueDataBuffer);
|
|
break;
|
|
|
|
case REG_DWORD:
|
|
if (cbValueData == sizeof(DWORD)) {
|
|
|
|
PutLiteral(s_DwordPrefix);
|
|
PutDword(*((LPDWORD) g_ValueDataBuffer), TRUE);
|
|
break;
|
|
|
|
}
|
|
// FALL THROUGH
|
|
|
|
case REG_BINARY:
|
|
default:
|
|
PutBinary((LPBYTE) g_ValueDataBuffer, Type, cbValueData);
|
|
break;
|
|
|
|
}
|
|
|
|
PutChar(TEXT('\n'));
|
|
|
|
if (g_FileErrorStringID == IDS_EXPFILEERRFILEWRITE)
|
|
return;
|
|
|
|
}
|
|
|
|
PutChar(TEXT('\n'));
|
|
|
|
if (RegError != ERROR_NO_MORE_ITEMS)
|
|
g_FileErrorStringID = IDS_EXPFILEERRREGENUM;
|
|
|
|
//
|
|
// Write out all of the subkeys and recurse into them.
|
|
//
|
|
|
|
lpSubKeyName = lpFullKeyName + STRLEN(lpFullKeyName);
|
|
*lpSubKeyName++ = TEXT('\\');
|
|
MaximumSubKeyLength = MAXKEYNAME - STRLEN(lpSubKeyName);
|
|
|
|
EnumIndex = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
if ((RegError = RegEnumKey(hKey, EnumIndex++, lpSubKeyName,
|
|
MaximumSubKeyLength)) != ERROR_SUCCESS)
|
|
break;
|
|
|
|
if(RegOpenKeyEx(hKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,&hSubKey) == ERROR_SUCCESS) {
|
|
|
|
PutBranch(hSubKey, lpFullKeyName);
|
|
|
|
RegCloseKey(hSubKey);
|
|
|
|
if (g_FileErrorStringID == IDS_EXPFILEERRFILEWRITE)
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
g_FileErrorStringID = IDS_EXPFILEERRREGOPEN;
|
|
|
|
}
|
|
|
|
if (RegError != ERROR_NO_MORE_ITEMS)
|
|
g_FileErrorStringID = IDS_EXPFILEERRREGENUM;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* PutLiteral
|
|
*
|
|
* DESCRIPTION:
|
|
* Writes a literal string to the registry file stream. No special handling
|
|
* is done for the string-- it is written out as is.
|
|
*
|
|
* PARAMETERS:
|
|
* lpLiteral, null-terminated literal to write to file.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutLiteral(
|
|
LPCTSTR lpLiteral
|
|
)
|
|
{
|
|
|
|
while (*lpLiteral != 0)
|
|
PutChar(*lpLiteral++);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* PutString
|
|
*
|
|
* DESCRIPTION:
|
|
* Writes a string to the registry file stream. A string is surrounded by
|
|
* double quotes and some characters may be translated to escape sequences
|
|
* to enable a parser to read the string back in.
|
|
*
|
|
* PARAMETERS:
|
|
* lpString, null-terminated string to write to file.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutString(
|
|
LPCTSTR lpString
|
|
)
|
|
{
|
|
|
|
TCHAR Char;
|
|
|
|
PutChar(TEXT('"'));
|
|
|
|
while ((Char = *lpString++) != 0) {
|
|
|
|
switch (Char) {
|
|
|
|
case TEXT('\\'):
|
|
case TEXT('"'):
|
|
PutChar(TEXT('\\'));
|
|
// FALL THROUGH
|
|
|
|
default:
|
|
PutChar(Char);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PutChar(TEXT('"'));
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* PutBinary
|
|
*
|
|
* DESCRIPTION:
|
|
* Writes a sequence of hexadecimal bytes to the registry file stream. The
|
|
* output is formatted such that it doesn't exceed a defined line length.
|
|
*
|
|
* PARAMETERS:
|
|
* lpBuffer, bytes to write to file.
|
|
* Type, value data type.
|
|
* cbBytes, number of bytes to write.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutBinary(
|
|
CONST BYTE FAR* lpBuffer,
|
|
DWORD Type,
|
|
DWORD cbBytes
|
|
)
|
|
{
|
|
|
|
BOOL fFirstByteOnLine;
|
|
BYTE Byte;
|
|
|
|
#ifdef UNICODE
|
|
// If we're writing one of the string formats that regedit doesn't write
|
|
// natively (but rather converts to a string of hex digits for streaming
|
|
// out), AND we're writing in downlevel/ANSI/REGEDIT4 format, we aren't
|
|
// going to write out the high byte of each (internally Unicode) character.
|
|
// So we will be writing half as many characters as the buffer byte size.
|
|
if (g_fSaveInDownlevelFormat &&
|
|
((Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ))) {
|
|
cbBytes = cbBytes / 2;
|
|
}
|
|
#endif
|
|
|
|
PutLiteral(s_HexPrefix);
|
|
|
|
if (Type != REG_BINARY) {
|
|
|
|
PutChar(TEXT('('));
|
|
PutDword(Type, FALSE);
|
|
PutChar(TEXT(')'));
|
|
|
|
}
|
|
|
|
PutChar(TEXT(':'));
|
|
|
|
fFirstByteOnLine = TRUE;
|
|
|
|
while (cbBytes--) {
|
|
|
|
if (s_FileIo.CurrentColumn > 75 && !fFirstByteOnLine) {
|
|
|
|
PutLiteral(s_FileLineBreak);
|
|
|
|
fFirstByteOnLine = TRUE;
|
|
|
|
}
|
|
|
|
if (!fFirstByteOnLine)
|
|
PutChar(TEXT(','));
|
|
|
|
Byte = *lpBuffer++;
|
|
|
|
#ifdef UNICODE
|
|
// If we're writing one of the string formats that regedit doesn't
|
|
// write natively (REG_EXPAND_SZ and REG_MULTI_SZ values get converted
|
|
// to a string of hex digits for streaming out), AND we're writing in
|
|
// downlevel/ANSI/REGEDIT4 format, we don't want to write out the high
|
|
// byte of each (internally Unicode) character. So in those cases, we
|
|
// advance another byte to get to the next ANSI character. Yes, this
|
|
// will lose data on non-SBCS characters, but that's what you get for
|
|
// saving in the downlevel format.
|
|
|
|
if (g_fSaveInDownlevelFormat &&
|
|
((Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ))) {
|
|
lpBuffer++;
|
|
}
|
|
#endif
|
|
|
|
PutChar(g_HexConversion[Byte >> 4]);
|
|
PutChar(g_HexConversion[Byte & 0x0F]);
|
|
|
|
fFirstByteOnLine = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* PutChar
|
|
*
|
|
* DESCRIPTION:
|
|
* Writes a 32-bit word to the registry file stream.
|
|
*
|
|
* PARAMETERS:
|
|
* Dword, dword to write to file.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutDword(
|
|
DWORD Dword,
|
|
BOOL fLeadingZeroes
|
|
)
|
|
{
|
|
|
|
int nTemp;
|
|
int CurrentNibble;
|
|
TCHAR Char;
|
|
BOOL fWroteNonleadingChar;
|
|
|
|
fWroteNonleadingChar = fLeadingZeroes;
|
|
|
|
for (CurrentNibble = 7; CurrentNibble >= 0; CurrentNibble--) {
|
|
|
|
nTemp = Dword;
|
|
Char = g_HexConversion[(nTemp >> (CurrentNibble * 4)) & 0x0F];
|
|
|
|
if (fWroteNonleadingChar || Char != TEXT('0')) {
|
|
|
|
PutChar(Char);
|
|
fWroteNonleadingChar = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We need to write at least one character, so if we haven't written
|
|
// anything yet, just spit out one zero.
|
|
//
|
|
|
|
if (!fWroteNonleadingChar)
|
|
PutChar(TEXT('0'));
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* PutChar
|
|
*
|
|
* DESCRIPTION:
|
|
* Writes one character to the registry file stream using an intermediate
|
|
* buffer.
|
|
*
|
|
* PARAMETERS:
|
|
* Char, character to write to file.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
PutChar(
|
|
TCHAR Char
|
|
)
|
|
{
|
|
|
|
//
|
|
// Keep track of what column we're currently at. This is useful in cases
|
|
// such as writing a large binary registry record. Instead of writing one
|
|
// very long line, the other Put* routines can break up their output.
|
|
//
|
|
|
|
if (Char != TEXT('\n'))
|
|
s_FileIo.CurrentColumn++;
|
|
|
|
else {
|
|
|
|
//
|
|
// Force a carriage-return, line-feed sequence to keep things like, oh,
|
|
// Notepad happy.
|
|
//
|
|
|
|
PutChar(TEXT('\r'));
|
|
|
|
s_FileIo.CurrentColumn = 0;
|
|
|
|
}
|
|
|
|
s_FileIo.Buffer[s_FileIo.BufferOffset++] = Char;
|
|
|
|
if (s_FileIo.BufferOffset == SIZE_FILE_IO_BUFFER)
|
|
FlushIoBuffer();
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FlushIoBuffer
|
|
*
|
|
* DESCRIPTION:
|
|
* Flushes the contents of the registry file stream to the disk and resets
|
|
* the buffer pointer.
|
|
*
|
|
* PARAMETERS:
|
|
* (none).
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
NEAR PASCAL
|
|
FlushIoBuffer(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
FILE_NUMBYTES NumberOfBytesWritten;
|
|
|
|
if (s_FileIo.BufferOffset) {
|
|
|
|
if (g_fSaveInDownlevelFormat)
|
|
{
|
|
//
|
|
// Convert Unicode to ANSI before writing.
|
|
//
|
|
|
|
int i;
|
|
|
|
#ifdef UNICODE
|
|
i = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
s_FileIo.Buffer,
|
|
s_FileIo.BufferOffset,
|
|
s_FileIo.ConversionBuffer,
|
|
sizeof(s_FileIo.ConversionBuffer),
|
|
NULL,
|
|
NULL
|
|
);
|
|
#else
|
|
lstrcpyn(s_FileIo.ConversionBuffer, s_FileIo.Buffer, s_FileIo.BufferOffset + 1);
|
|
i = lstrlen(s_FileIo.ConversionBuffer);
|
|
#endif //UNICODE
|
|
|
|
if (!WRITEFILE(s_FileIo.hFile, s_FileIo.ConversionBuffer, i,
|
|
&NumberOfBytesWritten) || (FILE_NUMBYTES) i !=
|
|
NumberOfBytesWritten)
|
|
|
|
g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Write Unicode text
|
|
//
|
|
if (!WRITEFILE(s_FileIo.hFile, s_FileIo.Buffer, s_FileIo.BufferOffset * sizeof(WCHAR),
|
|
&NumberOfBytesWritten) || (FILE_NUMBYTES) (s_FileIo.BufferOffset * sizeof(WCHAR)) !=
|
|
NumberOfBytesWritten)
|
|
g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
|
|
}
|
|
}
|
|
|
|
s_FileIo.BufferOffset = 0;
|
|
|
|
}
|