windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/tools/appdiff/appdiff.c

1683 lines
38 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
appdiff.c
Abstract:
Implements a stub tool that is designed to run with Win9x-side
upgrade code.
Author:
Jim Schmidt (jimschm) 26-Feb-1998
Revision History:
<alias> <date> <comments>
--*/
#include "pch.h"
#define S_FILES TEXT("Files")
#define S_REG TEXT("Reg")
#define S_INIFILES TEXT("IniFiles")
#define S_EXCLUDE TEXT("Exclude")
#define S_PATHS TEXT("Paths")
#define S_REGISTRY TEXT("Registry")
#define S_SUBSTITUTIONS TEXT("Substitutions")
#define S_SRC TEXT("Src")
#define S_DEST TEXT("Dest")
#define S_ADDED TEXT("Added")
#define S_CHANGED TEXT("Changed")
#define S_ZERO TEXT("0")
typedef struct {
BOOL SnapMode;
BOOL DiffMode;
BOOL CheckBits;
PCTSTR SnapFile;
PCTSTR AppFile;
PCTSTR Name;
PCTSTR OutputFile;
PCTSTR RegRoot;
PCTSTR FileSysRoot;
BOOL UseAppDiffInf;
BOOL NoRoots;
BOOL QuietMode;
} OPTIONS, *POPTIONS;
BOOL g_Quiet;
BOOL g_Thorough;
typedef struct {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
} FILEINFO, *PFILEINFO;
BOOL
DoSnapMode (
POPTIONS Options
);
BOOL
DoDiffMode (
POPTIONS Options
);
HANDLE g_hHeap;
HINSTANCE g_hInst;
BOOL
WINAPI
MigUtil_Entry (
HINSTANCE hInstance,
DWORD dwReason,
LPVOID lpReserved
);
BOOL
WINAPI
MemDb_Entry (
HINSTANCE hInstance,
DWORD dwReason,
LPVOID lpReserved
);
BOOL
Init (
VOID
)
{
HINSTANCE hInstance;
DWORD dwReason;
PVOID lpReserved;
//
// Simulate DllMain
//
hInstance = GetModuleHandle (NULL);
dwReason = DLL_PROCESS_ATTACH;
lpReserved = NULL;
g_hInst = hInstance;
g_hHeap = GetProcessHeap();
MigUtil_Entry (
hInstance,
dwReason,
lpReserved
);
MemDb_Entry (
hInstance,
dwReason,
lpReserved
);
return TRUE;
}
VOID
Terminate (
VOID
)
{
HINSTANCE hInstance;
DWORD dwReason;
PVOID lpReserved;
//
// Simulate DllMain
//
hInstance = GetModuleHandle (NULL);
dwReason = DLL_PROCESS_DETACH;
lpReserved = NULL;
MemDb_Entry (
hInstance,
dwReason,
lpReserved
);
MigUtil_Entry (
hInstance,
dwReason,
lpReserved
);
}
VOID
HelpAndExit (
VOID
)
{
printf ("Command line syntax:\n\n"
"appdiff -s[:snapfile] [-r:<regroot>] [-f:<fileroot>]\n"
"appdiff -d[:snapfile] [-a:appfilelist] [-n:name] [-o:outfile]\n"
"appdiff -s[:snapfile] -d [-n:name] [-o:outfile] [-r:<regroot>]\n"
" [-f:<fileroot>]\n"
"\n"
"-s Specifies snapshot mode, where snapfile is the name of\n"
" the memdb output file, and is snap.dat by default.\n"
"\n"
"-d Specifies diff mode, where snapfile is the name of a\n"
" previously generated snapshot file, and is snap.dat by\n"
" default.\n"
"\n"
"-a Specifies the application file list, as generated by\n"
" migfiles.exe.\n"
"\n"
"-n Specifies the application section name, and the default\n"
" is Application.\n"
"\n"
"-o Specifies the name of the output INF fragment, and the\n"
" default is output.inf\n"
"\n"
"-r Specifies a registry root to compare. If specified,\n"
" only the registry is scanned, unless -f is also specified.\n"
"\n"
"-f Specifies a file system root to compare. If specified,\n"
" only the file system is scanned, unless -r is also\n"
" specified.\n"
"\n"
"Additional Options:"
"\n"
"-q Quiet mode -- disables stderr output.\n"
"-u Use appdiff.inf and output.inf (for generation of uninstall\n"
" sections)\n"
"-t Thorough checks (computes checksums for all data)\n"
"\n"
"APPDIFF.INF specifies the registry and file system roots to scan on\n"
"a per-app basis, and is used to generate uninstall sections for\n"
"migdb.inf. See \\\\jimschm-dev\\team\\tools\\appdiff.inf for info.\n"
"\n"
"OUTPUT.INF is generated by this tool, and is designed to be cut &\n"
"pasted into migdb.inf.\n"
);
exit(0);
}
BOOL
pParseCommandLine (
IN INT ArgCount,
IN PTSTR ArgArray[],
OUT POPTIONS Options
)
{
INT i;
ZeroMemory (Options, sizeof (OPTIONS));
Options->NoRoots = TRUE;
for (i = 0 ; i < ArgCount ; i++) {
if (ArgArray[i][0] == TEXT('-') || ArgArray[i][0] == TEXT('/')) {
switch (_totlower (ArgArray[i][1])) {
case TEXT('s'):
Options->SnapMode = TRUE;
if (ArgArray[i][2] == TEXT(':')) {
if (Options->SnapFile) {
return FALSE;
}
Options->SnapFile = &ArgArray[i][3];
if (Options->SnapFile[0] == 0) {
return FALSE;
}
}
break;
case TEXT('t'):
if (g_Thorough) {
return FALSE;
}
Options->CheckBits = TRUE;
g_Thorough = TRUE;
break;
case TEXT('d'):
Options->DiffMode = TRUE;
if (ArgArray[i][2] == TEXT(':')) {
if (Options->SnapFile) {
return FALSE;
}
Options->SnapFile = &ArgArray[i][3];
if (Options->SnapFile[0] == 0) {
return FALSE;
}
}
break;
case TEXT('r'):
Options->NoRoots = FALSE;
if (Options->RegRoot) {
return FALSE;
}
if (ArgArray[i][2] == TEXT(':')) {
Options->RegRoot = &ArgArray[i][3];
} else if (i + 1 < ArgCount) {
i++;
Options->RegRoot = ArgArray[i];
} else {
Options->RegRoot = &ArgArray[i][2];
}
if (Options->RegRoot[0] == 0) {
return FALSE;
}
break;
case TEXT('f'):
Options->NoRoots = FALSE;
if (Options->FileSysRoot) {
return FALSE;
}
if (ArgArray[i][2] == TEXT(':')) {
Options->FileSysRoot = &ArgArray[i][3];
} else if (i + 1 < ArgCount) {
i++;
Options->FileSysRoot = ArgArray[i];
} else {
Options->FileSysRoot = &ArgArray[i][2];
}
if (Options->FileSysRoot[0] == 0) {
return FALSE;
}
break;
case TEXT('q'):
if (g_Quiet) {
return FALSE;
}
Options->QuietMode = TRUE;
g_Quiet = TRUE;
break;
case TEXT('u'):
if (Options->UseAppDiffInf) {
return FALSE;
}
Options->UseAppDiffInf = TRUE;
break;
case TEXT('a'):
if (Options->AppFile) {
return FALSE;
}
if (ArgArray[i][2] == TEXT(':')) {
Options->AppFile = &ArgArray[i][3];
} else if (i + 1 < ArgCount) {
i++;
Options->AppFile = ArgArray[i];
} else {
Options->AppFile = &ArgArray[i][2];
}
if (Options->AppFile[0] == 0) {
return FALSE;
}
break;
case TEXT('n'):
if (Options->Name) {
return FALSE;
}
if (ArgArray[i][2] == TEXT(':')) {
Options->Name = &ArgArray[i][3];
} else if (i + 1 < ArgCount) {
i++;
Options->Name = ArgArray[i];
} else {
Options->Name = &ArgArray[i][2];
}
if (Options->Name[0] == 0) {
return FALSE;
}
break;
case TEXT('o'):
if (Options->OutputFile) {
return FALSE;
}
if (ArgArray[i][2] == TEXT(':')) {
Options->OutputFile = &ArgArray[i][3];
} else if (i + 1 < ArgCount) {
i++;
Options->OutputFile = ArgArray[i];
} else {
Options->OutputFile = &ArgArray[i][2];
}
if (Options->OutputFile[0] == 0) {
return FALSE;
}
break;
default:
return FALSE;
}
}
else {
return FALSE;
}
}
if (!Options->SnapMode && !Options->DiffMode) {
return FALSE;
}
if (!Options->SnapFile) {
Options->SnapFile = TEXT("snap.dat");
}
if (!Options->OutputFile && Options->UseAppDiffInf) {
Options->OutputFile = TEXT("output.inf");
}
if (!Options->Name) {
Options->Name = TEXT("Application");
}
if (!g_Quiet) {
_ftprintf (stderr, TEXT("Snap file: %s\n"), Options->SnapFile);
if (Options->OutputFile) {
_ftprintf (stderr, TEXT("Output file: %s\n"), Options->OutputFile);
}
_ftprintf (stderr, TEXT("Thorough checks: %s\n"), Options->CheckBits ? "ENABLED" : "DISABLED");
_ftprintf (stderr, TEXT("Application Name: %s\n\n"), Options->Name);
}
return TRUE;
}
INT
__cdecl
_tmain (
INT argc,
TCHAR *argv[]
)
{
OPTIONS Options;
if (!pParseCommandLine (argc - 1, &argv[1], &Options)) {
HelpAndExit();
}
if (!Init()) {
printf ("Unable to initialize!\n");
return 255;
}
//
// Snap Mode: Gather the directory, registry and INI files
//
if (Options.SnapMode) {
DoSnapMode (&Options);
}
//
// Diff Mode: Gather another snapshot, then compare against
// original
//
if (Options.DiffMode) {
if (Options.SnapMode) {
_ftprintf (stderr, TEXT("Do your thing, then hit Enter.\n"));
getchar();
_ftprintf (stderr, TEXT("\n"));
}
DoDiffMode (&Options);
}
Terminate();
return 0;
}
BOOL
pCompareData (
IN PCBYTE Src,
IN PCBYTE Dest,
IN UINT Size
)
{
PCWSTR p, q;
if (Size >= sizeof (WCHAR)) {
p = (PCWSTR) (Src + Size - sizeof (WCHAR));
q = (PCWSTR) (Dest + Size - sizeof (WCHAR));
if (*p == 0 && *q == 0) {
if (StringIMatchW ((PCWSTR) Src, (PCWSTR) Dest)) {
return TRUE;
}
return FALSE;
}
}
return memcmp (Src, Dest, Size) == 0;
}
VOID
pSetMemDbKey (
IN BOOL DiffMode,
IN PCTSTR Group,
IN PCTSTR Key,
IN PBYTE Data,
IN DWORD DataSize
)
{
PCBYTE OrgData;
DWORD OrgSize;
TCHAR Node[MEMDB_MAX];
wsprintf (Node, TEXT("%s\\%s"), S_EXCLUDE, Key);
if (MemDbGetValue (Node, NULL)) {
return;
}
if (!DiffMode) {
MemDbSetBinaryValueEx (
S_ZERO,
Group,
Key,
Data,
DataSize,
NULL
);
}
else {
//
// Compare against original data
//
wsprintf (Node, TEXT("0\\%s\\%s"), Group, Key);
OrgData = MemDbGetBinaryValue (Node, &OrgSize);
if (!OrgData) {
//
// Data has been added
//
wsprintf (Node, TEXT("%s\\%s\\%s"), S_ADDED, Group, Key);
MemDbSetValue (Node, 0);
}
else {
//
// Delete memdb key, so remaining items will provide list of data
// that was deleted.
//
if (OrgSize != DataSize || !pCompareData (OrgData, Data, DataSize)) {
//
// Data has changed
//
MemDbDeleteValue (Node);
wsprintf (Node, TEXT("%s\\%s\\%s"), S_CHANGED, Group, Key);
MemDbSetValue (Node, 0);
} else {
//
// Data has not changed
//
MemDbDeleteValue (Node);
}
}
}
}
VOID
pConvertWin32FindData (
IN PWIN32_FIND_DATA Data,
OUT PFILEINFO Info
)
{
Info->dwFileAttributes = Data->dwFileAttributes;
Info->ftCreationTime = Data->ftCreationTime;
Info->ftLastWriteTime = Data->ftLastWriteTime;
Info->nFileSizeHigh = Data->nFileSizeHigh;
Info->nFileSizeLow = Data->nFileSizeLow;
}
VOID
pSetRegDataAndFreePtrs (
BOOL DiffMode,
PBYTE Data, OPTIONAL
PBYTE Data2, OPTIONAL
DWORD Size,
PCTSTR Key,
PCTSTR Value OPTIONAL
)
{
PCTSTR Node;
if (Data2) {
Node = CreateEncodedRegistryString (Key, Value);
pSetMemDbKey (
DiffMode,
S_REG,
Node,
Data2,
Size
);
FreeEncodedRegistryString (Node);
if (Data) {
MemFree (g_hHeap, 0, Data);
}
MemFree (g_hHeap, 0, Data2);
}
}
BOOL
pRegSnap (
BOOL DiffMode,
PCTSTR Root
)
{
REGTREE_ENUM Reg;
REGVALUE_ENUM RegVal;
PBYTE Data;
PBYTE Data2;
DWORD Size = 0;
TCHAR SkipTree[MEMDB_MAX];
TCHAR TempNode[MEMDB_MAX];
UINT SkipTreeBytes = 0;
SkipTree[0] = 0;
wsprintf (TempNode, TEXT("%s\\%s"), S_EXCLUDE, Root);
if (MemDbGetValue (TempNode, NULL)) {
return TRUE;
}
if (!g_Quiet) {
_ftprintf (stderr, TEXT("Taking snapshot of %s\n"), Root);
}
if (EnumFirstRegKeyInTree (&Reg, Root)) {
do {
//
// Key/key tree exclude processing
//
if (SkipTree[0]) {
if (StringIMatchByteCount (SkipTree, Reg.FullKeyName, SkipTreeBytes)) {
continue;
}
SkipTree[0] = 0;
}
wsprintf (TempNode, TEXT("%s\\%s"), S_EXCLUDE, Reg.FullKeyName);
if (MemDbGetValue (TempNode, NULL)) {
StringCopy (SkipTree, Reg.FullKeyName);
SkipTreeBytes = ByteCount (SkipTree);
continue;
}
//
// Non-excluded key
//
Data = NULL;
if (EnumFirstRegValue (&RegVal, Reg.CurrentKey->KeyHandle)) {
do {
Data = GetRegValueData (RegVal.KeyHandle, RegVal.ValueName);
Data2 = NULL;
if (Data) {
Size = RegVal.DataSize + sizeof (DWORD);
Data2 = MemAlloc (g_hHeap, 0, Size);
MYASSERT (Data2);
CopyMemory ((PDWORD) Data2 + 1, Data, RegVal.DataSize);
*((PDWORD) Data2) = RegVal.Type;
}
pSetRegDataAndFreePtrs (DiffMode, Data, Data2, Size, Reg.FullKeyName, RegVal.ValueName);
} while (EnumNextRegValue (&RegVal));
} else {
Size = sizeof (DWORD);
Data2 = MemAlloc (g_hHeap, 0, Size);
MYASSERT (Data2);
*((PDWORD) Data2) = 0xffffffff;
pSetRegDataAndFreePtrs (DiffMode, Data, Data2, Size, Reg.FullKeyName, RegVal.ValueName);
}
} while (EnumNextRegKeyInTree (&Reg));
}
return TRUE;
}
DWORD
pComputeChecksum (
PCTSTR FullPath
)
{
HANDLE File;
HANDLE Map;
PBYTE Data;
UINT Size;
UINT u;
DWORD Checksum = 0;
Data = MapFileIntoMemory (FullPath, &File, &Map);
if (!Data) {
return 0xFFFFFFFF;
}
Size = GetFileSize (File, NULL);
for (u = 0 ; u < Size ; u++) {
Checksum = _rotl (Checksum, 3);
Checksum ^= Data[u];
}
UnmapFile (Data, Map, File);
return Checksum;
}
BOOL
pDirAndIniSnap (
BOOL DiffMode,
PCTSTR Root
)
{
TREE_ENUM Dir;
PCTSTR p, q, r;
TCHAR SectionNames[32768];
TCHAR KeyNames[32768];
TCHAR KeyValue[4096];
TCHAR Node[MEMDB_MAX];
TCHAR ExcludeNode[MEMDB_MAX];
UINT Count;
FILEINFO fi;
TCHAR SkipTree[MEMDB_MAX];
UINT SkipTreeBytes = 0;
DWORD Checksum;
SkipTree[0] = 0;
wsprintf (ExcludeNode, TEXT("%s\\%s"), S_EXCLUDE, Root);
if (MemDbGetValue (ExcludeNode, NULL)) {
return TRUE;
}
//
// Take a snapshot of all dirs in drive specified by Root
//
if (!g_Quiet) {
_ftprintf (stderr, TEXT("Taking snapshot of %s\n"), Root);
}
if (EnumFirstFileInTree (&Dir, Root, NULL, TRUE)) {
do {
//
// Exclude processing
//
if (SkipTree[0]) {
if (StringIMatchByteCount (SkipTree, Dir.FullPath, SkipTreeBytes)) {
continue;
}
SkipTree[0] = 0;
}
if (Dir.Directory) {
wsprintf (ExcludeNode, TEXT("%s\\%s"), S_EXCLUDE, Dir.FullPath);
if (MemDbGetValue (ExcludeNode, NULL)) {
StringCopy (SkipTree, Dir.FullPath);
AppendWack (SkipTree);
SkipTreeBytes = ByteCount (SkipTree);
continue;
}
}
//
// Non-excluded file
//
if (g_Thorough) {
Checksum = pComputeChecksum (Dir.FullPath);
pSetMemDbKey (
DiffMode,
S_FILES,
Dir.FullPath,
(PBYTE) &Checksum,
sizeof (Checksum)
);
} else {
pConvertWin32FindData (Dir.FindData, &fi);
pSetMemDbKey (
DiffMode,
S_FILES,
Dir.FullPath,
(PBYTE) &fi,
sizeof (FILEINFO)
);
}
p = _tcsrchr (Dir.Name, TEXT('.'));
if (p) {
p = _tcsinc (p);
if (StringIMatch (p, TEXT("INI"))) {
//
// Found INI file, take a snapshot of it
//
if (!g_Quiet) {
_ftprintf (stderr, TEXT(" Taking snapshot of %s\n"), Dir.FullPath);
}
Count = GetPrivateProfileString (NULL, NULL, TEXT("\0"), SectionNames, 32768, Dir.FullPath);
SectionNames[Count] = 0;
SectionNames[Count + 1] = 0;
p = SectionNames;
while (*p) {
//
// Filter out dup sections
//
r = SectionNames;
while (r < p) {
if (StringIMatch (p, r)) {
break;
}
r = GetEndOfString (r) + 1;
}
if (r < p) {
if (!g_Quiet) {
_ftprintf (stderr, TEXT(" ***Duplicate section ignored: [%s]\n"), p);
}
p = GetEndOfString (p) + 1;
continue;
}
//
// Process each key in the section
//
Count = GetPrivateProfileString (
p,
NULL,
TEXT("\0"),
KeyNames,
32768,
Dir.FullPath
);
KeyNames[Count] = 0;
KeyNames[Count + 1] = 0;
q = KeyNames;
while (*q) {
//
// Ignore duplicate value names
//
r = KeyNames;
while (r < q) {
if (StringIMatch (q, r)) {
break;
}
r = GetEndOfString (r) + 1;
}
if (r < q) {
if (!g_Quiet) {
_ftprintf (stderr, TEXT(" ***Duplicate key ignored: [%s] %s\n"), p, q);
}
q = GetEndOfString (q) + 1;
continue;
}
GetPrivateProfileString (
p,
q,
TEXT(""),
KeyValue,
4096,
Dir.FullPath
);
wsprintf (Node, TEXT("%s\\[%s]\\%s"), Dir.FullPath, p, q);
pSetMemDbKey (
DiffMode,
S_INIFILES,
Node,
(PBYTE) KeyValue,
ByteCount (KeyValue) + sizeof (TCHAR)
);
q = GetEndOfString (q) + 1;
}
p = GetEndOfString (p) + 1;
}
}
}
} while (EnumNextFileInTree (&Dir));
}
return TRUE;
}
VOID
pCreateSubst (
IN PCTSTR Src,
IN PCTSTR Dest
)
{
DWORD Offset;
MemDbSetValueEx (S_SUBSTITUTIONS, S_DEST, Dest, NULL, 0, &Offset);
MemDbSetValueEx (S_SUBSTITUTIONS, S_SRC, Src, NULL, Offset, NULL);
}
BOOL
pTakeSnapshot (
POPTIONS Options,
BOOL DiffMode
)
{
HINF Inf;
INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
TCHAR Path[MAX_TCHAR_PATH];
PTSTR p, q;
TCHAR Section[256];
UINT Dirs = 0;
UINT RegRoots = 0;
TCHAR WinDir[MAX_TCHAR_PATH];
TCHAR SystemDir[MAX_TCHAR_PATH];
TCHAR System32Dir[MAX_TCHAR_PATH];
TCHAR SystemDrive[8];
TCHAR ProgramFilesDir[MAX_TCHAR_PATH];
GetWindowsDirectory (WinDir, MAX_TCHAR_PATH);
StringCopy (SystemDir, WinDir);
StringCopy (AppendWack (SystemDir), TEXT("system"));
StringCopy (System32Dir, SystemDir);
StringCat (System32Dir, TEXT("32"));
SystemDrive[0] = SystemDir[0];
SystemDrive[1] = TEXT(':');
SystemDrive[2] = 0;
StringCopy (ProgramFilesDir, SystemDrive);
StringCopy (AppendWack (ProgramFilesDir), TEXT("Program Files"));
pCreateSubst (WinDir, TEXT("%%WINDIR%%"));
pCreateSubst (SystemDir, TEXT("%%SYSTEMDIR%%"));
pCreateSubst (System32Dir, TEXT("%%SYSTEM32DIR%%"));
pCreateSubst (SystemDrive, TEXT("%%SYSTEMDRIVE%%"));
pCreateSubst (ProgramFilesDir, TEXT("%%PROGRAMFILES%%"));
if (Options->UseAppDiffInf) {
GetModuleFileName (NULL, Path, MAX_TCHAR_PATH);
p = _tcsrchr (Path, TEXT('\\'));
MYASSERT (p);
StringCopy (p + 1, TEXT("appdiff.inf"));
Inf = InfOpenInfFile (Path);
} else {
Inf = INVALID_HANDLE_VALUE;
}
if (Inf == INVALID_HANDLE_VALUE) {
//
// Take snapshot of file system and INI files
//
if (Options->FileSysRoot) {
pDirAndIniSnap (DiffMode, Options->FileSysRoot);
} else if (Options->NoRoots) {
pDirAndIniSnap (DiffMode, TEXT("C:\\"));
}
//
// Take snapshot of registry
//
if (Options->RegRoot) {
pRegSnap (DiffMode, Options->RegRoot);
} else if (Options->NoRoots) {
pRegSnap (DiffMode, TEXT("HKLM"));
pRegSnap (DiffMode, TEXT("HKU"));
}
}
else {
//
// Fill in the [Exclude] section
//
if (InfFindFirstLine (Inf, S_EXCLUDE, NULL, &is)) {
do {
p = InfGetLineText (&is);
MemDbSetValueEx (S_EXCLUDE, p, NULL, NULL, 0, NULL);
} while (InfFindNextLine (&is));
}
InfResetInfStruct (&is);
if (Options->Name) {
wsprintf (Section, TEXT("%s.%s"), Options->Name, S_EXCLUDE);
if (InfFindFirstLine (Inf, Section, NULL, &is)) {
do {
p = InfGetLineText (&is);
MemDbSetValueEx (S_EXCLUDE, p, NULL, NULL, 0, NULL);
} while (InfFindNextLine (&is));
}
InfResetInfStruct (&is);
}
//
// Fill in the [Substitutions] section
//
if (InfFindFirstLine (Inf, S_SUBSTITUTIONS, NULL, &is)) {
do {
p = InfGetStringField (&is, 0);
q = InfGetStringField (&is, 1);
pCreateSubst (p, q);
} while (InfFindNextLine (&is));
}
InfResetInfStruct (&is);
if (Options->Name) {
wsprintf (Section, TEXT("%s.%s"), Options->Name, S_EXCLUDE);
if (InfFindFirstLine (Inf, Section, NULL, &is)) {
do {
p = InfGetLineText (&is);
MemDbSetValueEx (S_EXCLUDE, p, NULL, NULL, 0, NULL);
} while (InfFindNextLine (&is));
}
InfResetInfStruct (&is);
}
//
// Enumerate the [Paths] section, use c:\ by default
//
if (InfFindFirstLine (Inf, S_PATHS, NULL, &is)) {
do {
p = InfGetLineText (&is);
pDirAndIniSnap (DiffMode, p);
Dirs++;
} while (InfFindNextLine (&is));
InfResetInfStruct (&is);
}
if (Options->Name) {
wsprintf (Section, TEXT("%s.%s"), Options->Name, S_PATHS);
if (InfFindFirstLine (Inf, Section, NULL, &is)) {
do {
p = InfGetLineText (&is);
pDirAndIniSnap (DiffMode, p);
Dirs++;
} while (InfFindNextLine (&is));
}
InfResetInfStruct (&is);
}
if (!Dirs) {
pDirAndIniSnap (DiffMode, TEXT("C:\\"));
}
//
// Enumerate the [Registry] section, use HKLM and HKU by default
//
if (InfFindFirstLine (Inf, S_REGISTRY, NULL, &is)) {
do {
p = InfGetLineText (&is);
pRegSnap (DiffMode, p);
RegRoots++;
} while (InfFindNextLine (&is));
InfResetInfStruct (&is);
}
if (Options->Name) {
wsprintf (Section, TEXT("%s.%s"), Options->Name, S_REGISTRY);
if (InfFindFirstLine (Inf, Section, NULL, &is)) {
do {
p = InfGetLineText (&is);
pRegSnap (DiffMode, p);
RegRoots++;
} while (InfFindNextLine (&is));
InfResetInfStruct (&is);
}
}
if (!RegRoots) {
pRegSnap (DiffMode, TEXT("HKLM"));
pRegSnap (DiffMode, TEXT("HKU"));
}
InfCloseInfFile (Inf);
}
InfCleanUpInfStruct (&is);
return TRUE;
}
PCTSTR
pPerformSubstitution (
PGROWLIST EnvVars,
PCTSTR OrgStr
)
{
PCTSTR PathStr;
PCTSTR NewPathString;
UINT Count;
UINT u;
PCTSTR Src;
PCTSTR Dest;
Count = GrowListGetSize (EnvVars);
PathStr = DuplicatePathString (OrgStr, 0);
MYASSERT (PathStr);
for (u = 0 ; u < Count ; u += 2) {
Src = GrowListGetString (EnvVars, u);
Dest = GrowListGetString (EnvVars, u + 1);
NewPathString = StringSearchAndReplace (PathStr, Src, Dest);
if (NewPathString) {
FreePathString (PathStr);
PathStr = NewPathString;
}
}
return PathStr;
}
VOID
pCreateEnvVars (
PGROWLIST EnvVars
)
{
MEMDB_ENUM e;
TCHAR Dest[MEMDB_MAX];
UINT Count;
UINT u;
UINT Len;
//
// Enumerate source strings
//
Count = 0;
if (MemDbGetValueEx (&e, S_SUBSTITUTIONS, S_SRC, NULL)) {
do {
MemDbBuildKeyFromOffset (e.dwValue, Dest, 2, NULL);
Len = ByteCount (e.szName);
for (u = 0 ; u < Count ; u += 2) {
if (ByteCount (GrowListGetString (EnvVars, u)) < Len) {
break;
}
}
if (u < Count) {
GrowListInsertString (EnvVars, u, e.szName);
GrowListInsertString (EnvVars, u + 1, Dest);
} else {
GrowListAppendString (EnvVars, e.szName);
GrowListAppendString (EnvVars, Dest);
}
Count += 2;
} while (MemDbEnumNextValue (&e));
}
}
VOID
pDecodeRegStr (
IN PCTSTR RegStr,
OUT PTSTR Key,
OUT PCTSTR *ValuePtr
)
{
PTSTR p;
PCTSTR Val = NULL;
StringCopy (Key, RegStr);
p = _tcschr (Key, TEXT('['));
if (p) {
Val = _tcsinc (p);
p = _tcsdec2 (Key, p);
while (p) {
if (_tcsnextc (p) != TEXT(' ')) {
break;
}
p = _tcsdec2 (Key, p);
}
*p = 0;
}
*ValuePtr = Val;
}
BOOL
pAreAllValuesInMemDb (
IN PCTSTR RegStr,
IN BOOL Encoded,
IN HKEY KeyHandle OPTIONAL
)
{
BOOL WeOpen = FALSE;
REGVALUE_ENUM e;
TCHAR Key[MAX_REGISTRY_KEY];
PCTSTR Value;
BOOL b = TRUE;
//
// If encoded, decode first.
//
if (Encoded) {
pDecodeRegStr (RegStr, Key, &Value);
} else {
StringCopy (Key, RegStr);
Value = NULL;
}
//
// If key not open, open now
//
if (!KeyHandle) {
KeyHandle = OpenRegKeyStr (Key);
WeOpen = TRUE;
if (!KeyHandle) {
return TRUE;
}
}
//
// if there is at least one value remaining, fail
//
b = !EnumFirstRegValue (&e, KeyHandle);
if (WeOpen) {
CloseRegKey (KeyHandle);
}
return b;
}
BOOL
pIsEntireSubKeyGone (
IN PCTSTR RegStr,
IN BOOL Encoded
)
{
TCHAR Key[MAX_REGISTRY_KEY];
PCTSTR Value;
HKEY KeyHandle;
//
// If encoded, decode now
//
if (Encoded) {
pDecodeRegStr (RegStr, Key, &Value);
} else {
StringCopy (Key, RegStr);
Value = NULL;
}
//
// Open key
//
KeyHandle = OpenRegKeyStr (Key);
if (!KeyHandle) {
return TRUE;
}
CloseRegKey (KeyHandle);
return FALSE;
}
VOID
pAppendThingsToDelete (
POPTIONS Options,
HANDLE File
)
{
MEMDB_ENUM e;
PCTSTR p;
GROWLIST EnvVars = GROWLIST_INIT;
TCHAR SkipKey[MEMDB_MAX];
UINT SkipKeyBytes = 0;
BOOL RegFlag;
BOOL AppendStar;
BOOL RemoveVal;
PCTSTR OutLine;
TCHAR KeyBuf[MAX_REGISTRY_KEY];
PCTSTR DontCare;
SkipKey[0] = 0;
//
// Generate substitution mapping
//
pCreateEnvVars (&EnvVars);
//
// Write section name
//
if (!Options->Name) {
return;
}
WriteFileString (File, TEXT("["));
WriteFileString (File, Options->Name);
WriteFileString (File, TEXT("]\r\n"));
//
// Write all the things in the deleted key
//
if (MemDbGetValueEx (&e, S_ZERO, NULL, NULL)) {
do {
p = _tcschr (e.szName, TEXT('\\'));
MYASSERT (p);
if (StringIMatchAB (S_REG, e.szName, p)) {
RegFlag = TRUE;
} else {
RegFlag = FALSE;
}
//
// Skip if this node is a subkey of a deleted key
//
p = _tcsinc (p);
if (SkipKey[0]) {
if (StringIMatchByteCount (SkipKey, p, SkipKeyBytes)) {
continue;
}
SkipKey[0] = 0;
}
RemoveVal = FALSE;
AppendStar = FALSE;
OutLine = p;
if (RegFlag) {
//
// If this is a registry key, and everything in
// the registry key has been deleted, then
// just write the one key with a star after it.
//
if (pIsEntireSubKeyGone (p, TRUE)) {
RemoveVal = TRUE;
AppendStar = TRUE;
}
//
// If it's a registry key, and all the subvalues
// are deleted, then just write the one key, but
// without a star.
//
else if (pAreAllValuesInMemDb (p, TRUE, NULL)) {
RemoveVal = TRUE;
}
}
//
// The value spec needs to be removed from the reg key
//
if (RemoveVal) {
pDecodeRegStr (p, KeyBuf, &DontCare);
OutLine = CreateEncodedRegistryString (KeyBuf, NULL);
//
// Workaround: CreateEncodedRegistryString always appends
// an asterisk, and we want to control when the asterisk
// appears.
//
p = _tcsrchr (OutLine, TEXT('*'));
if (p && p[1] == 0) {
p = _tcsdec2 (OutLine, p);
if (p) {
*((PTSTR) p) = 0;
}
}
//
// If this entire key is going to be deleted, then
// turn on SkipKey so the memdb nodes will be skipped.
//
if (AppendStar && SkipKey[0] == 0) {
StringCopy (SkipKey, OutLine);
AppendWack (SkipKey);
SkipKeyBytes = ByteCount (SkipKey);
}
}
//
// Perform substitution on the string
//
p = pPerformSubstitution (&EnvVars, OutLine);
MYASSERT (p);
if (RemoveVal) {
FreeEncodedRegistryString (OutLine);
}
//
// Write the file/reg key to the file
//
WriteFileString (File, p);
if (AppendStar) {
WriteFileString (File, TEXT("\\*"));
}
WriteFileString (File, TEXT("\r\n"));
FreePathString (p);
} while (MemDbEnumNextValue (&e));
}
//
// Write blank line at the end
//
WriteFileString (File, TEXT("\r\n"));
FreeGrowList (&EnvVars);
}
BOOL
pDumpDiffs (
VOID
)
{
MEMDB_ENUM e;
BOOL Changes = FALSE;
if (MemDbGetValueEx (&e, S_ZERO, NULL, NULL)) {
_tprintf (TEXT("Deleted Items:\n"));
Changes = TRUE;
do {
_tprintf (TEXT(" %s\n"), e.szName);
} while (MemDbEnumNextValue (&e));
}
if (MemDbGetValueEx (&e, S_ADDED, NULL, NULL)) {
_tprintf (TEXT("Added Items:\n"));
do {
_tprintf (TEXT(" %s\n"), e.szName);
} while (MemDbEnumNextValue (&e));
}
if (MemDbGetValueEx (&e, S_CHANGED, NULL, NULL)) {
_tprintf (TEXT("Changed Items:\n"));
do {
_tprintf (TEXT(" %s\n"), e.szName);
} while (MemDbEnumNextValue (&e));
}
return Changes;
}
BOOL
pGenerateInf (
POPTIONS Options
)
{
HANDLE File;
BOOL DelChanges;
//
// Dump changes to stdout
//
DelChanges = pDumpDiffs();
if (Options->OutputFile) {
//
// Write a section to our output file
//
File = CreateFile (
Options->OutputFile,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (File == INVALID_HANDLE_VALUE) {
_ftprintf (stderr, TEXT("Cannot generate %s, error %u\n"), Options->OutputFile, GetLastError());
return FALSE;
}
if (DelChanges) {
pAppendThingsToDelete (Options, File);
}
CloseHandle (File);
}
return TRUE;
}
BOOL
DoSnapMode (
POPTIONS Options
)
{
DWORD Start;
Start = GetTickCount();
if (!pTakeSnapshot (Options, FALSE)) {
return FALSE;
}
MemDbSave (Options->SnapFile);
if (!g_Quiet) {
_ftprintf (stderr, TEXT("Run time: %u seconds\n"), (GetTickCount() - Start) / 1000);
}
return TRUE;
}
BOOL
DoDiffMode (
POPTIONS Options
)
{
DWORD Start;
Start = GetTickCount();
if (GetFileAttributes (Options->SnapFile) == 0xffffffff) {
_ftprintf (stderr, TEXT("Bogus file arg: %s\n"), Options->SnapFile);
}
if (!Options->SnapMode) {
MemDbLoad (Options->SnapFile);
}
if (!pTakeSnapshot (Options, TRUE)) {
return FALSE;
}
pGenerateInf (Options);
if (!g_Quiet) {
_ftprintf (stderr, TEXT("Run time: %u seconds\n"), (GetTickCount() - Start) / 1000);
}
return TRUE;
}