/*++ 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: --*/ #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:] [-f:]\n" "appdiff -d[:snapfile] [-a:appfilelist] [-n:name] [-o:outfile]\n" "appdiff -s[:snapfile] -d [-n:name] [-o:outfile] [-r:]\n" " [-f:]\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; }