/*++ Copyright (c) 1997 Microsoft Corporation Module Name: inifiles.c Abstract: There are two major actions that are performed in this module. 1. Migration of settings from the INI files to the registry according to subkeys from HKLM\Software\Microsoft\Windows NT\CurrentVersion\IniFileMapping. Entry point : ProcessIniFileMapping 2. Processing of INI files not listed in the above key, changing paths to files that we moved during upgrade. Entry point : ConvertIniFiles Author: Jim Schmidt (jimschm) 11-Sept-1997 Revision History: jimschm 23-Sep-1998 Changed to use new fileops calinn 29-Jan-1998 Added lookup for Win95 registry calinn 19-Jan-1998 added support for Shell settings processing calinn 06-Oct-1997 rewrote the whole source calinn 11-May-1998 Added MergeIniSettings, various fixes --*/ #include "pch.h" #include "migmainp.h" #include "..\merge\mergep.h" #define DBG_INIFILES "IniFiles" #define DBG_MOVEINISETTINGS "IniFileMove" #define BufferIncrement 1024 BOOL pLoadIniFileBuffer ( IN PCTSTR FileName, IN PCTSTR SectName, IN PCTSTR KeyName, OUT PTSTR *OutBuff ); BOOL pCopyIniFileToRegistry ( IN HKEY KeyHandle, IN PCTSTR FileName, IN BOOL UserMode ); BOOL pTransferSectionByKey ( IN PCTSTR OrigFileName, IN PCTSTR FileName, IN PCTSTR Section, IN HKEY SectionKey, IN OUT BOOL *IniFileChanged, IN BOOL UserMode ); BOOL pTransferSectionByValue ( IN PCTSTR OrigFileName, IN PCTSTR FileName, IN PCTSTR Section, IN PCTSTR SectionValue, IN OUT BOOL *IniFileChanged, IN BOOL UserMode ); BOOL pSaveMappedValue ( IN PCTSTR OrigFileName, IN PCTSTR Section, IN PCTSTR ValueName, IN PCTSTR RegPath, IN PCTSTR Value, IN OUT BOOL *ReverseMapping, OUT PTSTR *ReverseMapValue, IN BOOL UserMode ); BOOL pBuildSuppressionTable ( IN BOOL UserMode ); VOID pFreeSuppressionTable ( VOID ); BOOL pIncompatibleShell ( VOID ); BOOL pIncompatibleSCR ( VOID ); BOOL ProcessIniFileMapping ( IN BOOL UserMode ) /*++ Routine Description: ProcessIniFileMapping reads in the INI files, copying data to specific locations in the registry. This copy is based on the IniFileMapping key in Software\Microsoft\Windows NT\CurrentVersion. These mappings can be overridden based upon the content of the [Suppress Ini File Mappings] section. Arguments: UserMode - Specifies TRUE if per-user sections are to be processed, or FALSE if local machine sections are to be processed. Return Value: Always returns TRUE. If some error occured while processing, then there will be a log entry specifying that. --*/ { REGKEY_ENUM e; HKEY IniMappingKey; HKEY OldRegRoot=NULL; BOOL Result = TRUE; DEBUGMSG ((DBG_INIFILES, "Processing INI file mapping - START")); if (UserMode) { OldRegRoot = GetRegRoot(); SetRegRoot (g_hKeyRootNT); } __try { if (!EnumFirstRegKeyStr (&e, S_INIFILEMAPPING_KEY)) { // // nothing to do here // __leave; } // // There is at least one file mapping to process. // Fill the table of ini file suppression table. // __try { // // Trying to load the suppression table, recording the eventual error // but going on with the stuff // if (!pBuildSuppressionTable(UserMode)) { Result = FALSE; } // Special case : SHELL= line from SYSTEM.INI // We try to see if the current shell is supported on NT. // If not then we will add SHELL to this suppression table // ensuring that the NT registry setting will get mapped into // the INI file if ((!UserMode) && (pIncompatibleShell()) ) { MemDbSetValueEx ( MEMDB_CATEGORY_SUPPRESS_INI_MAPPINGSW, TEXT("SYSTEM.INI"), TEXT("BOOT"), TEXT("SHELL"), 0, NULL ); } if ((UserMode) && (pIncompatibleSCR())) { MemDbSetValueEx ( MEMDB_CATEGORY_SUPPRESS_INI_MAPPINGSW, TEXT("SYSTEM.INI"), TEXT("BOOT"), TEXT("SCRNSAVE.EXE"), 0, NULL ); } // // Now processing keys // do { IniMappingKey = OpenRegKey (e.KeyHandle, e.SubKeyName); if (IniMappingKey) { // // Process the file mapping // if (!pCopyIniFileToRegistry ( IniMappingKey, e.SubKeyName, UserMode )) { Result = FALSE; } CloseRegKey(IniMappingKey); } else { DEBUGMSG ((DBG_INIFILES, "IniFileMapping : Could not open %s", e.SubKeyName)); Result = FALSE; } } while (EnumNextRegKey (&e)); pFreeSuppressionTable(); } __finally { AbortRegKeyEnum (&e); } } __finally { if (UserMode) { SetRegRoot (OldRegRoot); } } DEBUGMSG ((DBG_INIFILES, "Processing INI file mapping - STOP")); if (!Result) { // // we are going to log that at least one error occured while processing IniFileMapping // DEBUGMSG ((DBG_ERROR, (PCSTR)MSG_INI_FILE_MAPPING_LOG)); } return TRUE; } BOOL pIncompatibleShell ( VOID ) { TCHAR key [MEMDB_MAX]; TCHAR shellVal[MEMDB_MAX] = TEXT(""); PCTSTR fileName; PCTSTR newName; DWORD result; fileName = JoinPaths (g_WinDir, TEXT("SYSTEM.INI")); newName = GetTemporaryLocationForFile (fileName); if (newName) { DEBUGMSG ((DBG_INIFILES, "pIncompatibleShell: Using %s for %s", newName, fileName)); FreePathString (fileName); fileName = newName; } if (!DoesFileExist (fileName)) { DEBUGMSG ((DBG_INIFILES, "pIncompatibleShell: %s not found", fileName)); } result = GetPrivateProfileString ( TEXT("boot"), TEXT("shell"), TEXT("explorer.exe"), shellVal, MEMDB_MAX, fileName); FreePathString (fileName); if ((result == 0) || (result + 1 == MEMDB_MAX) ) { return FALSE; } MemDbBuildKey (key, MEMDB_CATEGORY_COMPATIBLE_SHELL_NT, shellVal, NULL, NULL); return (!MemDbGetValue (key, NULL)); } BOOL pIncompatibleSCR ( VOID ) { TCHAR scrVal [MEMDB_MAX] = TEXT(""); PCTSTR fileName; PCTSTR newName; DWORD result; fileName = JoinPaths (g_WinDir, TEXT("SYSTEM.INI")); newName = GetTemporaryLocationForFile (fileName); if (newName) { DEBUGMSG ((DBG_INIFILES, "pIncompatibleSCR: Using %s for %s", newName, fileName)); FreePathString (fileName); fileName = newName; } if (!DoesFileExist (fileName)) { DEBUGMSG ((DBG_INIFILES, "pIncompatibleSCR: %s not found", fileName)); } result = GetPrivateProfileString ( TEXT("boot"), TEXT("SCRNSAVE.EXE"), TEXT(""), scrVal, MEMDB_MAX, fileName); FreePathString (fileName); if ((result == 0) || (result + 1 == MEMDB_MAX) ) { return FALSE; } return IsFileMarkedForDelete (scrVal); } BOOL pLoadIniFileBuffer ( IN PCTSTR FileName, IN PCTSTR SectName, IN PCTSTR KeyName, OUT PTSTR *OutBuff ) /*++ Routine Description: This routine uses GetPrivateProfileString routine trying to load a section buffer, a key buffer or a key value (depends on the arguments). The reason why there is such a routine is to be sure that we are able to load stuff from INI file while not allocating a lot of memory. This routine incrementaly allocates memory, returning when there is enough memory to load stuff from INI file. Arguments: FileName - Specifies INI file to be processed SectName - Specifies section to be processed. If NULL the whole section buffer will be loaded KeyName - Specifies the key to be processed. If NULL the whole key buffer will be loaded. OutBuff - Output buffer holding the result. Return Value: TRUE if successful, FALSE otherwise --*/ { DWORD OutBuffSize; DWORD ReadSize; BOOL Done; OutBuffSize = 0; *OutBuff = NULL; do { if (*OutBuff) { MemFree (g_hHeap, 0, *OutBuff); } OutBuffSize += BufferIncrement; *OutBuff = MemAlloc (g_hHeap, 0, OutBuffSize * sizeof (TCHAR)); if (!(*OutBuff)) { return FALSE; } ReadSize = GetPrivateProfileString ( SectName, KeyName, TEXT(""), *OutBuff, OutBuffSize, FileName ); if (SectName && KeyName) { Done = (ReadSize < OutBuffSize - 1); } else { Done = (ReadSize < OutBuffSize - 2); } } while (!Done); return TRUE; } BOOL pCopyIniFileToRegistry ( IN HKEY KeyHandle, IN PCTSTR FileName, IN BOOL UserMode ) /*++ Routine Description: This routine transports settings from the INI file into registry or from the registry to the INI file. Arguments: KeyHandle - IniFileMapping key associated with this INI file. FileName - Specifies INI file to be processed UserMode - Specifies TRUE if per-user sections are to be processed, or FALSE if local machine sections are to be processed. Return Value: TRUE if successful, FALSE if at least one error occured --*/ { PCTSTR NewName = NULL; PCTSTR FullPath = NULL; TCHAR TempPath[MEMDB_MAX]; PTSTR Section, SectionBuf; HKEY SectionKey; PCTSTR SectionValue; DWORD Attribs = -1; BOOL IniFileChanged = FALSE; BOOL mapToIniFile = FALSE; BOOL Result = TRUE; DEBUGMSG ((DBG_INIFILES, "Processing %s - START", FileName)); // // now we have the full path for the INI file // Since we are going to use Ini file API we have to copy every file to some other name // to avoid mapping requests into registry // if (!GetTempFileName (g_WinDir, TEXT("INI"), 0, TempPath)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot create a temporary file")); return FALSE; } __try { FullPath = JoinPaths (g_WinDir, FileName); NewName = GetTemporaryLocationForFile (FullPath); if (NewName) { DEBUGMSG ((DBG_INIFILES, "Using %s for %s", NewName, FullPath)); FreePathString (FullPath); FullPath = NewName; } Attribs = GetFileAttributes (FullPath); if (Attribs == (DWORD)-1) { DEBUGMSG ((DBG_INIFILES, "pCopyIniFileToRegistry: %s not found", FullPath)); __leave; } // // now trying to copy file // if (!CopyFile (FullPath, TempPath, FALSE)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot copy %s to %s", FullPath, TempPath)); Result = FALSE; __leave; } SetFileAttributes (TempPath, FILE_ATTRIBUTE_NORMAL); __try { // // Next thing we are going to do is to load the sections in a buffer // if (!pLoadIniFileBuffer (TempPath, NULL, NULL, &SectionBuf)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot load section buffer for %s", TempPath)); return FALSE; } __try { // // now we have all sections of the INI file and proceeding // Section = SectionBuf; // // there is a loop here for every section in the buffer // while (*Section) { // // now trying to see if there is a subkey matching section name. // SectionKey = OpenRegKey (KeyHandle, Section); if (SectionKey) { if (!pTransferSectionByKey ( FileName, TempPath, Section, SectionKey, &IniFileChanged, UserMode )) { Result = FALSE; } CloseRegKey (SectionKey); } else { SectionValue = GetRegValueString (KeyHandle, Section); if (!SectionValue) { SectionValue = GetRegValueString (KeyHandle, S_EMPTY); } if (SectionValue && (*SectionValue)) { if (!pTransferSectionByValue ( FileName, TempPath, Section, SectionValue, &IniFileChanged, UserMode )) { Result = FALSE; } } if (SectionValue) { MemFree (g_hHeap, 0, SectionValue); } } Section = GetEndOfString (Section) + 1; } } __finally { if (SectionBuf) { MemFree (g_hHeap, 0 , SectionBuf); } } // // finally, if we made any changes then we will copy the INI file back // if (IniFileChanged) { // flushing the INI file WritePrivateProfileString ( NULL, NULL, NULL, TempPath ); if (!CopyFile (TempPath, FullPath, FALSE)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot copy %s to %s", TempPath, FullPath)); return FALSE; } } } __finally { SetFileAttributes (TempPath, FILE_ATTRIBUTE_NORMAL); DeleteFile (TempPath); } } __finally { if (Attribs != (DWORD)-1) { SetFileAttributes (FullPath, Attribs); } if (FullPath) { FreePathString (FullPath); } SetFileAttributes (TempPath, FILE_ATTRIBUTE_NORMAL); DeleteFile (TempPath); DEBUGMSG ((DBG_INIFILES, "Processing %s - STOP", FileName)); } return Result; } BOOL pTransferSectionByKey ( IN PCTSTR OrigFileName, IN PCTSTR FileName, IN PCTSTR Section, IN HKEY SectionKey, IN OUT BOOL *IniFileChanged, IN BOOL UserMode ) /*++ Routine Description: This routine transports settings from a specified section of an INI file into registry or from the registry to the INI file. If there is a case when the settings go from registry to INI file then IniFileChanged is set to TRUE Arguments: FileName - Specifies INI file to be processed Section - Specifies section to be processed SectionKey - key associated with this section IniFileChanged - Tells the caller that at least one setting was from registry to the INI file UserMode - Specifies TRUE if per-user sections are to be processed, or FALSE if local machine sections are to be processed. Return Value: TRUE if successful, FALSE if at least one error occured --*/ { PTSTR Key, KeyBuf; PTSTR KeyValue; PCTSTR SectionValue; BOOL ReverseMapping; PTSTR ReverseMapValue; BOOL Result = TRUE; if (!pLoadIniFileBuffer (FileName, Section, NULL, &KeyBuf)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot load key buffer for %s in %s", Section, FileName)); return FALSE; } __try { // // now we have all keys of the section and proceeding // Key = KeyBuf; // // there is a loop here for every key in the buffer // while (*Key) { // // trying to read the value for the key // if (!pLoadIniFileBuffer (FileName, Section, Key, &KeyValue)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot load key %s in %s in %s", Key, Section, FileName)); Result = FALSE; continue; } __try { SectionValue = GetRegValueString (SectionKey, Key); if (!SectionValue) { SectionValue = GetRegValueString (SectionKey, S_EMPTY); } if (SectionValue && (*SectionValue)) { if (!pSaveMappedValue ( OrigFileName, Section, Key, SectionValue, KeyValue, &ReverseMapping, &ReverseMapValue, UserMode )) { Result = FALSE; } if (UserMode && ReverseMapping) { if (!WritePrivateProfileString ( Section, Key, NULL, FileName )) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot erase key %s from %s from %s", Key, Section, OrigFileName)); Result = FALSE; } else { *IniFileChanged = TRUE; } } else { if ((ReverseMapping) && (ReverseMapValue)) { // writing the new value if (!WritePrivateProfileString ( Section, Key, ReverseMapValue, FileName )) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot write line %s=%s in %s in %s", Key, ReverseMapValue, Section, FileName)); Result = FALSE; } else { *IniFileChanged = TRUE; } } } if (ReverseMapValue) { MemFree (g_hHeap, 0, ReverseMapValue); } } if (SectionValue) { MemFree (g_hHeap, 0, SectionValue); } } __finally { if (KeyValue) { MemFree (g_hHeap, 0, KeyValue); } } Key = GetEndOfString (Key) + 1; } } __finally { if (KeyBuf) { MemFree (g_hHeap, 0, KeyBuf); } } return Result; } BOOL pTransferSectionByValue ( IN PCTSTR OrigFileName, IN PCTSTR FileName, IN PCTSTR Section, IN PCTSTR SectionValue, IN OUT BOOL *IniFileChanged, IN BOOL UserMode ) /*++ Routine Description: This routine transports settings from a specified section of an INI file into registry or from the registry to the INI file. If there is a case when the settings go from registry to INI file then IniFileChanged is set to TRUE Arguments: FileName - Specifies INI file to be processed Section - Specifies section to be processed SectionValue - ValueName associated with this section IniFileChanged - Tells the caller that at least one setting was from registry to the INI file UserMode - Specifies TRUE if per-user sections are to be processed, or FALSE if local machine sections are to be processed. Return Value: TRUE if successful, FALSE if at least one error occured --*/ { PTSTR Key, KeyBuf; PTSTR KeyValue; BOOL ReverseMapping; PTSTR ReverseMapValue; BOOL Result = TRUE; if (!pLoadIniFileBuffer (FileName, Section, NULL, &KeyBuf)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot load key buffer for %s in %s", Section, FileName)); return FALSE; } __try { // // now we have all keys of the section and proceeding // Key = KeyBuf; // // there is a loop here for every key in the buffer // while (*Key) { // // trying to read the value for the key // if (!pLoadIniFileBuffer (FileName, Section, Key, &KeyValue)) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot load key %s in %s in %s", Key, Section, FileName)); Result = FALSE; continue; } __try { if (!pSaveMappedValue ( OrigFileName, Section, Key, SectionValue, KeyValue, &ReverseMapping, &ReverseMapValue, UserMode )) { Result = FALSE; } if (UserMode && ReverseMapping) { if (!WritePrivateProfileString ( Section, Key, NULL, FileName )) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot erase key %s from %s from %s", Key, Section, OrigFileName)); Result = FALSE; } else { *IniFileChanged = TRUE; } } else { if ((ReverseMapping) &&(ReverseMapValue)) { // writing the new value if (!WritePrivateProfileString ( Section, Key, ReverseMapValue, FileName )) { DEBUGMSG ((DBG_ERROR,"Ini File Mapping : Cannot write line %s=%s in %s in %s", Key, ReverseMapValue, Section, FileName)); Result = FALSE; } else { *IniFileChanged = TRUE; } } } if (ReverseMapValue) { MemFree (g_hHeap, 0, ReverseMapValue); } } __finally { if (KeyValue) { MemFree (g_hHeap, 0, KeyValue); } } Key = GetEndOfString (Key) + 1; } } __finally { MemFree (g_hHeap, 0, KeyBuf); } return Result; } BOOL pDoesStrHavePrefix ( IN OUT PCTSTR *String, IN PCTSTR Prefix ) /*++ Routine Description: Simple routine that checks if a specified string has a specified prefix and if so advances the string pointer to point exactly after the prefix. Arguments: String - String to be processed Prefix - Prefix to be processed Return Value: TRUE if String has Prefix, FALSE otherwise --*/ { UINT Len; Len = CharCount (Prefix); if (StringIMatchCharCount (*String, Prefix, Len)) { *String = CharCountToPointer (*String, Len); return TRUE; } return FALSE; } BOOL pShouldSaveKey ( IN PCTSTR OrigFileName, IN PCTSTR Section, IN PCTSTR ValueName, IN PCTSTR RegKey, OPTIONAL IN OUT BOOL *ReverseMapping, IN BOOL UserMode, IN BOOL ExclusionsOnly ) /*++ Routine Description: Simple routine that checks if a setting should go from INI file to registry. If the setting is globally suppressed or it's in suppression table returns FALSE Arguments: OrigFileName - Specifies the original Win9x INI file name (not the current one) Section - Specifies the section within the INI file ValueName - Specifies the key within the INI file section RegKey - Specifies the registry key destination, from the IniFileMapping key; optional only if ExclusionsOnly is TRUE ReverseMapping - Receives TRUE if the direction of data copy is to go from the NT registry to the INI file; receives FALSE if the direction is from the INI file to the registry UserMode - Specifies TRUE to do per-user processing ExclusionsOnly - Specifies TRUE if only exclusions should be tested Return Value: TRUE if direction is from INI file to registry, FALSE otherwise --*/ { HKEY key; HKEY OldRegRoot = NULL; BOOL b = TRUE; TCHAR ekey[MEMDB_MAX]; LONG rc; *ReverseMapping = FALSE; if (RegKey && IsNtRegObjectSuppressed (RegKey, NULL)) { DEBUGMSG ((DBG_NAUSEA, "INI destination is suppressed: %s", RegKey)); return FALSE; } // // Let's see if this mapping is suppressed // MemDbBuildKey ( ekey, MEMDB_CATEGORY_SUPPRESS_INI_MAPPINGSW, OrigFileName, Section, ValueName ); if (MemDbGetStoredEndPatternValue (ekey, NULL)) { DEBUGMSG (( DBG_NAUSEA, "INI destination is suppressed: %s\\%s\\%s", OrigFileName, Section, ValueName )); *ReverseMapping = TRUE; return FALSE; } // // If the NT key exists and we don't want to overwrite NT values, reverse // the mapping. // MemDbBuildKey ( ekey, MEMDB_CATEGORY_NO_OVERWRITE_INI_MAPPINGSW, OrigFileName, Section, ValueName ); if (MemDbGetStoredEndPatternValue (ekey, NULL)) { if (ExclusionsOnly) { return FALSE; } if (UserMode) { OldRegRoot = GetRegRoot(); SetRegRoot (g_hKeyRootNT); } key = OpenRegKeyStr (RegKey); if (key) { rc = RegQueryValueEx (key, ValueName, NULL, NULL, NULL, NULL); if (rc == ERROR_SUCCESS) { // // The NT registry value exists, do not overwrite it. // Instead, reverse the mapping so that the INI file // gets the NT value. // DEBUGMSG (( DBG_NAUSEA, "NT value exists; reversing mapping: %s [%s]", RegKey, ValueName )); *ReverseMapping = TRUE; // // don't write the key on return, instead write in INI file // b = FALSE; } CloseRegKey (key); } if (UserMode) { SetRegRoot (OldRegRoot); } return b; } if (ExclusionsOnly) { return TRUE; } // // If Win9x key exists, reverse the mapping (so the Win9x registry setting // is used instead of the potentially stale INI file setting) // if (UserMode) { OldRegRoot = GetRegRoot(); SetRegRoot (g_hKeyRoot95); } key = OpenRegKeyStr95 (RegKey); if (UserMode) { SetRegRoot (OldRegRoot); } if (key != NULL) { if (Win95RegQueryValueEx (key, ValueName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { *ReverseMapping = TRUE; DEBUGMSG ((DBG_NAUSEA, "INI destination is suppressed: %s", RegKey)); b = FALSE; } CloseRegKey95 (key); } DEBUGMSG_IF ((b, DBG_NAUSEA, "INI destination is not suppressed: %s", RegKey)); return b; } BOOL pSaveMappedValue ( IN PCTSTR OrigFileName, IN PCTSTR Section, IN PCTSTR ValueName, IN PCTSTR RegPath, IN PCTSTR Value, IN OUT BOOL *ReverseMapping, OUT PTSTR *ReverseMapValue, IN BOOL UserMode ) /*++ Routine Description: This routine has a valuename and a value that should be saved in a key indicated by RegPath. Arguments: RegPath - Key where setting should be saved ValueName - ValueName for the key Value - Value for the key ReverseMapping - tells the caller that the setting should be saved from registry to INI file ReverseMapValue - if ReverseMapping is TRUE that we have the value of the key here UserMode - Specifies TRUE if per-user sections are to be processed, or FALSE if local machine sections are to be processed. Return Value: TRUE if success, FALSE otherwise --*/ { CHARTYPE ch; TCHAR RegKey[MAX_REGISTRY_KEY]; DWORD rc; HKEY SaveKey; PCTSTR newValue; PTSTR p; BOOL Result = TRUE; *ReverseMapping = FALSE; *ReverseMapValue = NULL; // // Parse the string // // // Skip past special chars // while (TRUE) { ch = (CHARTYPE)_tcsnextc (RegPath); if (ch == TEXT('!') || ch == TEXT('#') || ch == TEXT('@') ) { RegPath = _tcsinc (RegPath); } else { break; } } // // If SYS:, USR: or \Registry\Machine\ then replace appropriately // RegKey[0] = 0; if (pDoesStrHavePrefix (&RegPath, TEXT("SYS:"))) { if (UserMode) { return TRUE; } p = TEXT("HKLM\\SOFTWARE"); } else if (pDoesStrHavePrefix (&RegPath, TEXT("USR:"))) { if (!UserMode) { return TRUE; } p = TEXT("HKR"); } else if (pDoesStrHavePrefix (&RegPath, TEXT("\\Registry\\Machine\\"))) { if (UserMode) { return TRUE; } p = TEXT("HKLM"); } _sntprintf (RegKey, MAX_REGISTRY_KEY, TEXT("%s\\%s"), p, RegPath); if (pShouldSaveKey(OrigFileName, Section, ValueName, RegKey, ReverseMapping, UserMode, FALSE)) { SaveKey = CreateRegKeyStr (RegKey); if (SaveKey) { newValue = GetPathStringOnNt (Value); rc = RegSetValueEx ( SaveKey, ValueName, 0, REG_SZ, (PBYTE) newValue, SizeOfString (newValue) ); CloseRegKey (SaveKey); if (rc != ERROR_SUCCESS) { SetLastError (rc); Result = FALSE; DEBUGMSG (( DBG_ERROR, "Process Ini File Mapping: Could not save %s=%s to %s", ValueName, newValue, RegKey )); } FreePathString (newValue); } else { DEBUGMSG ((DBG_ERROR, "Process Ini File Mapping: Could not create %s", RegKey)); } } else { if (*ReverseMapping) { // trying to open key SaveKey = OpenRegKeyStr (RegKey); if (SaveKey) { *ReverseMapValue = (PTSTR)GetRegValueString (SaveKey, ValueName); CloseRegKey (SaveKey); } } } return Result; } BOOL pBuildSuppressionTable ( IN BOOL UserMode ) /*++ Routine Description: Loads the "Suppress INI File Mappings" section from "wkstamig.inf" or from "usermig.inf" into a stringtable. Arguments: UserMode - Specifies TRUE if section is loaded from usermig.inf FALSE if section is loaded from wkstamig.inf UserMode Return Value: Always returns TRUE. In case of an error, we are going to log it but returning TRUE trying to go on. --*/ { HINF InfHandle; TCHAR field[MEMDB_MAX]; INFCONTEXT context; if (UserMode) { InfHandle = g_UserMigInf; } else { InfHandle = g_WkstaMigInf; } if (InfHandle == INVALID_HANDLE_VALUE) { DEBUGMSG((DBG_ERROR,"Ini File Mapping : wkstamig.inf or usermig.inf is not loaded")); return FALSE; } if (SetupFindFirstLine (InfHandle, S_SUPPRESS_INI_FILE_MAPPINGS, NULL, &context)) { do { if (SetupGetStringField (&context, 0, field, MEMDB_MAX, NULL)) { MemDbSetValueEx ( MEMDB_CATEGORY_SUPPRESS_INI_MAPPINGSW, field, NULL, NULL, 0, NULL ); } } while (SetupFindNextLine (&context, &context)); } if (SetupFindFirstLine (InfHandle, S_NO_OVERWRITE_INI_FILE_MAPPINGS, NULL, &context)) { do { if (SetupGetStringField (&context, 0, field, MEMDB_MAX, NULL)) { MemDbSetValueEx ( MEMDB_CATEGORY_NO_OVERWRITE_INI_MAPPINGSW, field, NULL, NULL, 0, NULL ); } } while (SetupFindNextLine (&context, &context)); } return TRUE; } VOID pFreeSuppressionTable ( VOID ) /*++ Routine Description: Simple routine that free the string table if it exists Arguments: none Return Value: none --*/ { MemDbDeleteTree (MEMDB_CATEGORY_SUPPRESS_INI_MAPPINGSW); MemDbDeleteTree (MEMDB_CATEGORY_NO_OVERWRITE_INI_MAPPINGSW); } enum Separators { tab = TEXT('\t'), space = TEXT(' '), comma = TEXT(','), quote = TEXT('\"') }; BOOL pAddValue( IN OUT TCHAR **Buffer, IN OUT TCHAR *Value, IN UINT BufChars ); BOOL pProcessStrValue ( IN TCHAR *InBuf, OUT TCHAR *OutBuf, IN UINT BufChars ); BOOL ConvertIniFile ( IN PCTSTR IniFilePath ); BOOL pIsDosFullPathPattern ( IN PCTSTR String ) /*++ Routine Description: pIsDosFullPathPattern checks if a string may be a valid DOS full path, i.e. a drive letter folowed by a colon and a backslash. Arguments: String - Specifies the string to be tested Return Value: TRUE if the string may represent a valid full DOS path, FALSE if not. --*/ { return String && *String && String[1] == TEXT(':') && String[2] == TEXT('\\'); } BOOL ConvertIniFiles ( VOID ) /*++ Routine Description: ConvertIniFiles reads in the INI files not listed in IniFileMapping key, and converts every full path file name to it's new value if it has been moved during instalation. Calls ConvertIniFile for each INI file from Windows directory not listed in IniFileMapping. This function is mainly for compatibility with older programs using INI files instead of registry Arguments: none Return Value: TRUE if successful, FALSE if at least one error occured while processing. The function will continue even if an error occures while processing a particular ini file trying to get the job done as much as possible. --*/ { FILEOP_ENUM fe; FILEOP_PROP_ENUM eOpProp; MEMDB_ENUM e; PCTSTR NtPath; PCTSTR filePtr = NULL; PCTSTR extPtr = NULL; PCTSTR winDirWack = NULL; BOOL result = TRUE; winDirWack = JoinPaths (g_WinDir, TEXT("")); if (EnumFirstPathInOperation (&fe, OPERATION_TEMP_PATH)) { do { filePtr = GetFileNameFromPath (fe.Path); if (!filePtr) { continue; } extPtr = GetFileExtensionFromPath (fe.Path); if (!extPtr) { continue; } if (StringIMatch (extPtr, TEXT("INI"))) { // this is an INI file that was relocated. Let's process it. if (EnumFirstFileOpProperty (&eOpProp, fe.Sequencer, OPERATION_TEMP_PATH)) { // even if Result is false we keep trying to update the file DEBUGMSG ((DBG_INIFILES, "ConvertIniFile: %s (temp=%s)", fe.Path, eOpProp.Property)); // // see comments at the beginning of MergeIniFile // if (DoesFileExist (eOpProp.Property)) { if (!ConvertIniFile(eOpProp.Property)) { result = FALSE; } } else { if (EnumNextFileOpProperty (&eOpProp)) { if (!ConvertIniFile(eOpProp.Property)) { result = FALSE; } } ELSE_DEBUGMSG (( DBG_WHOOPS, "ConvertIniFiles: Couldn't get final destination for %s", fe.Path )); } } } } while (EnumNextPathInOperation (&fe)); } FreePathString (winDirWack); // // also convert all INI files listed in MEMDB_CATEGORY_INIFILES_CONVERT // if (MemDbGetValueEx (&e, MEMDB_CATEGORY_INIFILES_CONVERT, NULL, NULL)) { do { NtPath = GetPathStringOnNt (e.szName); DEBUGMSG ((DBG_INIFILES, "ConvertIniFile: Nt=%s (Win9x=%s)", NtPath, e.szName)); if (!ConvertIniFile (NtPath)) { result = FALSE; } FreePathString (NtPath); } while (MemDbEnumNextValue (&e)); } if (!result) { // // we are going to log that at least one error occured while processing IniFileConversion // DEBUGMSG ((DBG_ERROR, (PCSTR)MSG_INI_FILE_CONVERSION_LOG)); } return TRUE; } BOOL ConvertIniFile ( IN PCTSTR IniFilePath ) /*++ Routine Description: ConvertIniFile reads in the INI file received and converts every full path file name to it's new value if it has been moved during instalation. It also applies all string substitutions specified in [String Map] section of wkstamig.inf This function is called from ConvertIniFiles. Arguments: IniFilePath - Specifies INI file that is to be processed Return Value: TRUE if successful, FALSE if at least one error occured while processing. The function will continue even if an error occures while processing a particular ini file trying to get the job done as much as possible. --*/ { PTSTR Section = NULL; PTSTR SectionBuf = NULL; PTSTR SectionDest = NULL; PTSTR Key = NULL; PTSTR KeyBuf = NULL; PTSTR KeyDest = NULL; BOOL IniFileChanged = FALSE; BOOL Result = TRUE; DWORD status; TCHAR InValueBuf[MEMDB_MAX]; TCHAR OutValueBuf[MEMDB_MAX]; TCHAR TempPath[MEMDB_MAX]; DWORD Attribs; // // we want to have ready two full paths: // 1. full path to ini file that we are processing (Ex: c:\windows\setup\tmp00001) // 2. full path to ini file temporary name while processing (system generated) // if (!DoesFileExist (IniFilePath)) { DEBUGMSG ((DBG_INIFILES, "ConvertIniFile: %s not found", IniFilePath)); return TRUE; } if (!GetTempFileName (g_WinDir, TEXT("INI"), 0, TempPath)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot create a temporary file")); return FALSE; } __try { // // first of all we copy this INI file to be sure that GetPrivateProfileString function // does not map our requests into registry // if (!CopyFile (IniFilePath, TempPath, FALSE)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot copy %s to %s", IniFilePath, TempPath)); Result = FALSE; __leave; } Attribs = GetFileAttributes (TempPath); MYASSERT (Attribs != (DWORD)-1); SetFileAttributes (TempPath, FILE_ATTRIBUTE_NORMAL); // // now trying to get section buffer from the INI file // We will try to get section buffer in a 1024 bytes buffer. If this is not enough then // we will increase buffer size with 1024 and so on. // if (!pLoadIniFileBuffer (IniFilePath, NULL, NULL, &SectionBuf)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot load section buffer for %s", IniFilePath)); Result = FALSE; __leave; } // // now we have all sections of the INI file and proceeding // Section = SectionBuf; // // there is a loop here for every section in the buffer // while (*Section) { // // section name can also contain paths // if (pIsDosFullPathPattern (Section)) { status = GetFileStatusOnNt (Section); } else { status = FILESTATUS_UNCHANGED; } if (status & FILESTATUS_DELETED) { // // delete the whole section // if (!WritePrivateProfileString (Section, NULL, NULL, TempPath)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot delete section %s in %s", Section, IniFilePath)); Result = FALSE; } IniFileChanged = TRUE; } else { // // now trying to get key buffer for this section // KeyBuf = NULL; if (!pLoadIniFileBuffer (IniFilePath, Section, NULL, &KeyBuf)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot load key buffer for %s in %s", Section, IniFilePath)); Result = FALSE; __leave; } // // section name may contain paths // SectionDest = Section; if (pProcessStrValue (Section, OutValueBuf, MEMDB_MAX)) { // // use this new section name // SectionDest = DuplicateText (OutValueBuf); MYASSERT (SectionDest); IniFileChanged = TRUE; // // delete the whole old section before continuing // if (!WritePrivateProfileString (Section, NULL, NULL, TempPath)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot delete section %s in %s", Section, IniFilePath)); Result = FALSE; } IniFileChanged = TRUE; } // // now we have all keys from this section and proceeding // Key = KeyBuf; // // there is a loop here for every key in the section // while (*Key) { // // key name can also contain paths // if (pIsDosFullPathPattern (Key)) { status = GetFileStatusOnNt (Key); } else { status = FILESTATUS_UNCHANGED; } if (status & FILESTATUS_DELETED) { // // delete the key // if (!WritePrivateProfileString (SectionDest, Key, NULL, TempPath)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot delete key %s in section %s in %s", Key, SectionDest, IniFilePath)); Result = FALSE; } IniFileChanged = TRUE; } else { KeyDest = Key; if (pProcessStrValue (Key, OutValueBuf, MEMDB_MAX)) { // // use this new key name // KeyDest = DuplicateText (OutValueBuf); MYASSERT (KeyDest); IniFileChanged = TRUE; // // deleting the previous key // if (!WritePrivateProfileString ( SectionDest, Key, NULL, TempPath )) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot delete line %s in %s in %s", Key, SectionDest, IniFilePath)); Result = FALSE; } } GetPrivateProfileString( Section, Key, TEXT(""), InValueBuf, MEMDB_MAX, IniFilePath ); // // let's see if the key value is a deleted file. // If so, we will simply delete the key. // if (pIsDosFullPathPattern (InValueBuf)) { status = GetFileStatusOnNt (InValueBuf); } else { status = FILESTATUS_UNCHANGED; } if (status & FILESTATUS_DELETED) { // // deleting the old key // if (!WritePrivateProfileString ( SectionDest, KeyDest, NULL, TempPath )) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot delete line %s in %s in %s", KeyDest, SectionDest, IniFilePath)); Result = FALSE; } IniFileChanged = TRUE; } else { // // now we are going to make a lexical analysis of this value string // to see if there are some candidates (e.g. full path file names) // To find out if there is a full file name we will just see if the second // and the third characters are : respectively \ // if (pProcessStrValue (InValueBuf, OutValueBuf, MEMDB_MAX) || KeyDest != Key || SectionDest != Section ) { // // writing the new value // if (!WritePrivateProfileString ( SectionDest, KeyDest, OutValueBuf, TempPath )) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot write line %s=%s in %s in %s", KeyDest, OutValueBuf, SectionDest, IniFilePath)); Result = FALSE; } IniFileChanged = TRUE; } } if (KeyDest != Key) { FreeText (KeyDest); KeyDest = NULL; } } Key = GetEndOfString (Key) + 1; KeyDest = NULL; } if (SectionDest != Section) { FreeText (SectionDest); SectionDest = NULL; } if (KeyBuf) { MemFree (g_hHeap, 0, KeyBuf); KeyBuf = NULL; } } Section = GetEndOfString (Section) + 1; SectionDest = NULL; } if (SectionBuf) { MemFree (g_hHeap, 0, SectionBuf); SectionBuf = NULL; } // // finally, if we made any changes then we will copy the INI file back // if (IniFileChanged) { // // flushing the INI file // WritePrivateProfileString (NULL, NULL, NULL, TempPath); SetFileAttributes (TempPath, Attribs); SetFileAttributes (IniFilePath, FILE_ATTRIBUTE_NORMAL); if (!CopyFile (TempPath, IniFilePath, FALSE)) { DEBUGMSG ((DBG_ERROR,"Convert Ini File : Cannot copy %s to %s", TempPath, IniFilePath)); Result = FALSE; __leave; } } } __finally { SetFileAttributes (TempPath, FILE_ATTRIBUTE_NORMAL); DeleteFile (TempPath); if (KeyDest && KeyDest != Key) { FreeText (KeyDest); KeyDest = NULL; } if (SectionDest && SectionDest != Section) { FreeText (SectionDest); SectionDest = NULL; } if (KeyBuf) { MemFree (g_hHeap, 0, KeyBuf); KeyBuf = NULL; } if (SectionBuf) { MemFree (g_hHeap, 0, SectionBuf); SectionBuf = NULL; } } return Result; } BOOL pLookupStrValue ( IN PCTSTR Value, OUT PTSTR OutBuffer, OUT PINT OutBytes, IN UINT OutBufferSize ) { if (MappingSearchAndReplaceEx ( g_CompleteMatchMap, Value, OutBuffer, 0, OutBytes, OutBufferSize, STRMAP_COMPLETE_MATCH_ONLY, NULL, NULL )) { return TRUE; } return MappingSearchAndReplaceEx ( g_SubStringMap, Value, OutBuffer, 0, OutBytes, OutBufferSize, STRMAP_ANY_MATCH, NULL, NULL ); } BOOL pProcessStrValue ( IN TCHAR *InBuf, OUT TCHAR *OutBuf, IN UINT BufChars ) /*++ Routine Description: Simple lex that identifies lexems separated by comma, space, tab and quote. For each lexem calls a function that can change the value of the lexem. OBS: When between quote's comma,space and tab are not considered separators This function is called from ConvertIniFile. Arguments: InBuf - Specifies buffer to be processed OutBuf - Specifies buffer to hold the result BufChars - Specifies the size of OutBuf in chars Return Value: TRUE if there was any change. --*/ { TCHAR OrgLexem[MEMDB_MAX]; TCHAR *Lexem; // Status = 0 - initial state // Status = 1 - processing a string between quotes // Status = 2 - processing a normal string INT Status; BOOL Result = FALSE; // // first check to see if the whole string should be replaced; // some paths contain spaces, even if they are not between quotes // Lexem = OutBuf; if (pAddValue (&Lexem, InBuf, BufChars)) { *Lexem = 0; return TRUE; } Lexem = OrgLexem; Status = 0; for (;;) { *Lexem = 0; *OutBuf = 0; switch (*InBuf) { case 0: Result |= pAddValue(&OutBuf, OrgLexem, MEMDB_MAX); *OutBuf = 0; return Result; case quote: if (Status == 0) { Status = 1; *OutBuf = *InBuf; InBuf++; OutBuf++; break; } Result |= pAddValue(&OutBuf, OrgLexem, MEMDB_MAX); Lexem = OrgLexem; if (Status == 1) { *OutBuf = *InBuf; InBuf++; OutBuf++; } Status = 0; break; case space: case comma: case tab: if (Status == 1) { *Lexem = *InBuf; Lexem++; InBuf++; break; }; if (Status == 0) { *OutBuf = *InBuf; InBuf++; OutBuf++; break; }; Result |= pAddValue(&OutBuf, OrgLexem, MEMDB_MAX); Lexem = OrgLexem; *OutBuf = *InBuf; InBuf++; OutBuf++; Status = 0; break; default: if (Status ==0) { Status = 2; }; *Lexem = *InBuf; Lexem++; InBuf++; } } } BOOL pAddValue( IN OUT TCHAR **Buffer, IN OUT TCHAR *Value, IN UINT BufChars ) /*++ Routine Description: Simple routine that takes a string value, modifies it (or not) and that adds it to a buffer. This function is called from pProcessStrValue Arguments: Buffer - Specifies buffer to hold the value Value - Specifies the string value to be processed Return Value: TRUE if there was any change. --*/ { DWORD fileStatus; PTSTR newValue, Source; INT OutBytes; BOOL Result = FALSE; // // replaced (Value[0]) && (!_tcsncmp (Value + 1, TEXT(":\\"), 2)) with the call below // for consistency // if (pIsDosFullPathPattern (Value)) { fileStatus = GetFileStatusOnNt (Value); if ((fileStatus & FILESTATUS_MOVED) == FILESTATUS_MOVED) { Result = TRUE; newValue = GetPathStringOnNt (Value); // // advance outbound pointer // Source = newValue; while (*Source) { **Buffer = *Source; (*Buffer)++; Source++; } FreePathString (newValue); } } if (!Result) { // // try to map this sub-string // if (pLookupStrValue ( Value, *Buffer, &OutBytes, BufChars * sizeof (TCHAR) )) { Result = TRUE; MYASSERT (OutBytes % sizeof (TCHAR) == 0); *Buffer += OutBytes / sizeof (TCHAR); } } if (!Result) { while (*Value) { **Buffer = *Value; (*Buffer)++; Value++; } } return Result; } BOOL pMoveIniSettingsBySection ( IN PCWSTR Section ) { INFCONTEXT context; WCHAR srcData [MEMDB_MAX]; WCHAR destData [MEMDB_MAX]; WCHAR destValue[MEMDB_MAX]; WCHAR tempPathS[MEMDB_MAX]; WCHAR tempPathD[MEMDB_MAX]; INT adnlData = 0; LONG value; PWSTR valuePtr; PCWSTR srcFile; PCWSTR srcSect; PCWSTR srcKey; PCWSTR destFile; PCWSTR destSect; PCWSTR destKey; PWSTR tempPtr; PCWSTR srcFullPath = NULL; PCWSTR destFullPath = NULL; PCWSTR newPath = NULL; PTSTR sect, sectionBuf; PTSTR key, keyBuf; PCWSTR destSectFull; PCWSTR destKeyFull; BOOL iniFileChanged; DWORD result; if (SetupFindFirstLine (g_WkstaMigInf, Section, NULL, &context)) { do { if ((SetupGetStringField (&context, 0, srcData, MEMDB_MAX, NULL)) && (SetupGetStringField (&context, 1, destData, MEMDB_MAX, NULL)) ) { // // We now have a line like : \\ = \\ // __try { *tempPathS = 0; *tempPathD = 0; iniFileChanged = FALSE; srcFile = srcData; tempPtr = wcschr (srcData, L'\\'); if (!tempPtr) { __leave; } srcSect = tempPtr + 1; *tempPtr = 0; tempPtr = wcschr (srcSect, L'\\'); if (!tempPtr) { __leave; } srcKey = tempPtr + 1; *tempPtr = 0; destFile = destData; tempPtr = wcschr (destData, L'\\'); if (!tempPtr) { __leave; } destSect = tempPtr + 1; *tempPtr = 0; tempPtr = wcschr (destSect, L'\\'); if (!tempPtr) { __leave; } destKey = tempPtr + 1; *tempPtr = 0; srcFullPath = JoinPaths (g_WinDir, srcFile); newPath = GetTemporaryLocationForFile (srcFullPath); if (newPath) { DEBUGMSG ((DBG_MOVEINISETTINGS, "Using %s for %s", newPath, srcFullPath)); FreePathString (srcFullPath); srcFullPath = newPath; } if (!DoesFileExist (srcFullPath)) { DEBUGMSG ((DBG_INIFILES, "pMoveIniSettingsBySection: %s not found", srcFullPath)); __leave; } destFullPath = JoinPaths (g_WinDir, destFile); newPath = GetTemporaryLocationForFile (destFullPath); if (newPath) { DEBUGMSG ((DBG_MOVEINISETTINGS, "pMoveIniSettingsBySection: Using %s for %s", newPath, destFullPath)); FreePathString (destFullPath); destFullPath = newPath; } // Copy Source File to a temporary location to avoid registry mapping if (!GetTempFileName (g_WinDir, TEXT("INI"), 0, tempPathS)) { DEBUGMSG ((DBG_ERROR,"pMoveIniSettingsBySection: Cannot create a temporary file")); __leave; } if (!CopyFile (srcFullPath, tempPathS, FALSE)) { DEBUGMSG ((DBG_ERROR,"pMoveIniSettingsBySection: Cannot copy %s to %s", srcFullPath, tempPathS)); __leave; } // Copy Destination File to a temporary location to avoid registry mapping if (!GetTempFileName (g_WinDir, TEXT("INI"), 0, tempPathD)) { DEBUGMSG ((DBG_ERROR,"pMoveIniSettingsBySection: Cannot create a temporary file")); __leave; } if (!CopyFile (destFullPath, tempPathD, FALSE)) { DEBUGMSG ((DBG_INIFILES,"pMoveIniSettingsBySection: Cannot copy %s to %s", destFullPath, tempPathD)); } // if we have an additional field we use it for dividing the key values (if they are numbers) if (!SetupGetIntField (&context, 3, &adnlData)) { adnlData = 0; } // // Next thing we are going to do is to load the sections in a buffer // if (!pLoadIniFileBuffer (tempPathS, NULL, NULL, §ionBuf)) { DEBUGMSG ((DBG_ERROR,"pMoveIniSettingsBySection: Cannot load section buffer for %s (%s)", tempPathS, srcFullPath)); __leave; } // // now walk through each section // __try { sect = sectionBuf; // // there is a loop here for every section in the buffer // while (*sect) { if (IsPatternMatch (srcSect, sect)) { // // Next thing we are going to do is to load the keys in a buffer // if (!pLoadIniFileBuffer (tempPathS, sect, NULL, &keyBuf)) { DEBUGMSG ((DBG_ERROR,"pMoveIniSettingsBySection: Cannot load key buffer for %s in %s (%s)", sect, tempPathS, srcFullPath)); __leave; } __try { // // now we have all keys of the section and proceeding // key = keyBuf; // // there is a loop here for every key in the buffer // while (*key) { if (IsPatternMatch (srcKey, key)) { result = GetPrivateProfileString ( sect, key, TEXT(""), destValue, MEMDB_MAX, tempPathS); if ((result == 0) || (result + 1 == MEMDB_MAX) ) { DEBUGMSG (( DBG_MOVEINISETTINGS, "pMoveIniSettingsBySection: Cannot read value for %s in %s in %s (%s)", key, sect, tempPathS, srcFullPath )); } else { if (adnlData) { value = wcstol (destValue, &valuePtr, 10); if (*valuePtr == 0) { value = value / adnlData; wsprintf (destValue, L"%d", value); } } destSectFull = StringSearchAndReplace (destSect, L"*", sect); if (!destSectFull) { destSectFull = DuplicatePathString (destSect,0); } destKeyFull = StringSearchAndReplace (destKey, L"*", key); if (!destKeyFull) { destKeyFull = DuplicatePathString (destKey,0); } iniFileChanged = TRUE; // writing the new value if (!WritePrivateProfileString ( destSectFull, destKeyFull, destValue, tempPathD )) { DEBUGMSG (( DBG_ERROR, "Ini File Move : Cannot write line %s=%s in %s in %s (%s)", destKeyFull, destValue, destSectFull, tempPathD, destFullPath )); FreePathString (destSectFull); FreePathString (destKeyFull); __leave; } FreePathString (destSectFull); FreePathString (destKeyFull); } } key = GetEndOfString (key) + 1; } } __finally { if (keyBuf) { MemFree (g_hHeap, 0 , keyBuf); } } } sect = GetEndOfString (sect) + 1; } } __finally { if (sectionBuf) { MemFree (g_hHeap, 0 , sectionBuf); } } if (iniFileChanged) { // flushing the INI file WritePrivateProfileString ( NULL, NULL, NULL, tempPathD ); if (!CopyFile (tempPathD, destFullPath, FALSE)) { DEBUGMSG ((DBG_ERROR,"Ini File Move : Cannot copy %s to %s", tempPathD, destFullPath)); __leave; } } } __finally { if (srcFullPath) { FreePathString (srcFullPath); srcFullPath = NULL; } if (destFullPath) { FreePathString (destFullPath); destFullPath = NULL; } if (*tempPathS) { SetFileAttributes (tempPathS, FILE_ATTRIBUTE_NORMAL); DeleteFile (tempPathS); } if (*tempPathD) { SetFileAttributes (tempPathD, FILE_ATTRIBUTE_NORMAL); DeleteFile (tempPathD); } } } } while (SetupFindNextLine (&context, &context)); } return TRUE; } BOOL MoveIniSettings ( VOID ) /*++ Routine Description: There are a number of settings that needs to be moved from one INI file to another during setup. There is a section called "MoveIniSettings" in wkstamig.inf that lists those settings. The format is \section\key = \section\key You can use pattern matching is section and key (INI file must be specified in full). The only wild character supported in right term is * and is going to be replaced by the equivalent left term. For example if you specify: foo.ini\FooSect\FooKey = bar.ini\*\* then the FooKey key from FooSect section from foo.ini is going to be moved to bar.ini. This is useful to move a whole section : foo.ini\FooSect\* = bar.ini\*\* We are going to use Get/WritePrivateProfileString because we want that all the settings to be mapped into the registry is it's the case (this routine is called after IniFileMapping routine). This routine is called before IniFileConversion routine so the moved settings are Win95 ones. Arguments: None Return Value: FALSE if any error occured. --*/ { WCHAR codePageStr [20] = L""; PWSTR codePageSection = NULL; MYASSERT (g_WkstaMigInf != INVALID_HANDLE_VALUE); pMoveIniSettingsBySection (S_MOVEINISETTINGS); _itow (OurGetACP (), codePageStr, 10); codePageSection = JoinTextEx (NULL, S_MOVEINISETTINGS, codePageStr, L".", 0, NULL); pMoveIniSettingsBySection (codePageSection); FreeText (codePageSection); return TRUE; } BOOL MergeIniSettings ( VOID ) { FILEOP_ENUM fe; FILEOP_PROP_ENUM eOpProp; PCTSTR filePtr; PCTSTR extPtr; PCTSTR winDirWack; PCTSTR NtPath; BOOL Win9xPriority; BOOL result = TRUE; // // Process INI files that were moved to temporary dir // winDirWack = JoinPaths (g_WinDir, TEXT("")); if (EnumFirstPathInOperation (&fe, OPERATION_TEMP_PATH)) { do { if (!pBuildSuppressionTable(FALSE)) { result = FALSE; } // Special case : SHELL= line from SYSTEM.INI // We try to see if the current shell is supported on NT. // If not then we will add SHELL to this suppression table // ensuring that the NT registry setting will get mapped into // the INI file if (pIncompatibleShell()) { MemDbSetValueEx ( MEMDB_CATEGORY_SUPPRESS_INI_MAPPINGSW, TEXT("SYSTEM.INI"), TEXT("BOOT"), TEXT("SHELL"), 0, NULL ); } filePtr = GetFileNameFromPath (fe.Path); if (!filePtr) { continue; } extPtr = GetFileExtensionFromPath (fe.Path); if (!extPtr) { continue; } if (StringIMatch (extPtr, TEXT("INI"))) { // this is an INI file that was relocated. Let's process it. if (EnumFirstFileOpProperty (&eOpProp, fe.Sequencer, OPERATION_TEMP_PATH)) { if (StringIMatch (filePtr, TEXT("desktop.ini"))) { Win9xPriority = FALSE; } else { Win9xPriority = !StringIMatchAB (winDirWack, fe.Path, filePtr); } NtPath = GetPathStringOnNt (fe.Path); if (!MergeIniFile(NtPath, eOpProp.Property, Win9xPriority)) { result = FALSE; } FreePathString (NtPath); } } } while (EnumNextPathInOperation (&fe)); pFreeSuppressionTable(); } FreePathString (winDirWack); return TRUE; } PTSTR pMapIniSectionKeyToRegistryKey ( IN PCTSTR FileName, IN PCTSTR Section, IN PCTSTR Key ) { CHARTYPE ch; TCHAR RegKey[MAX_REGISTRY_KEY] = S_EMPTY; DWORD rc; HKEY key, sectKey; PTSTR keyStr; PTSTR regPath; PTSTR data = NULL; PTSTR p; keyStr = JoinPaths (S_INIFILEMAPPING_KEY, FileName); __try { key = OpenRegKeyStr (keyStr); if (!key) { __leave; } sectKey = OpenRegKey (key, Section); if (sectKey) { data = GetRegValueString (sectKey, Key); if (!data) { data = GetRegValueString (sectKey, S_EMPTY); } CloseRegKey (sectKey); } else { data = GetRegValueString (key, Section); if (!data) { data = GetRegValueString (key, S_EMPTY); } } if (data) { // // convert it to a reg key string // regPath = data; // // Skip past special chars // while (TRUE) { ch = (CHARTYPE)_tcsnextc (regPath); if (ch == TEXT('!') || ch == TEXT('#') || ch == TEXT('@') ) { regPath = _tcsinc (regPath); } else { break; } } // // If SYS:, USR: or \Registry\Machine\ then replace appropriately // if (pDoesStrHavePrefix (®Path, TEXT("SYS:"))) { p = TEXT("HKLM\\SOFTWARE"); } else if (pDoesStrHavePrefix (®Path, TEXT("USR:"))) { p = TEXT("HKR"); } else if (pDoesStrHavePrefix (®Path, TEXT("\\Registry\\Machine\\"))) { p = TEXT("HKLM"); } _sntprintf (RegKey, MAX_REGISTRY_KEY, TEXT("%s\\%s"), p, regPath); } } __finally { if (key) { CloseRegKey (key); } if (data) { MemFree (g_hHeap, 0, data); } FreePathString (keyStr); keyStr = NULL; } if (RegKey[0]) { keyStr = DuplicateText (RegKey); } return keyStr; } BOOL MergeIniFile ( IN PCTSTR FileNtLocation, IN PCTSTR FileTempLocation, IN BOOL TempHasPriority ) { TCHAR TempPath[MEMDB_MAX]; TCHAR srcValue[MEMDB_MAX]; TCHAR destValue[MEMDB_MAX]; DWORD Attribs = -1; PTSTR Section, SectionBuf; PTSTR Key, KeyBuf; BOOL Result = TRUE; BOOL IniFileChanged = FALSE; PTSTR regKey; PTSTR p; PCTSTR fileName; // // sometimes, textmode setup doesn't move files from other drives to Windows drive, // probably because textmode setup drive mapping doesn't match Win9x drive mappings. // It's possible that the INI file hasn't actually been moved, so in this case there // is nothing to do // There is no data loss, since the file is actually not moved and it's converted // in place in ConvertIniFiles // if (*g_WinDir != *FileNtLocation && !DoesFileExist (FileTempLocation) && DoesFileExist (FileNtLocation) ) { // // done, file is already in place // return TRUE; } // // some desktop.ini are located in temp internet dirs that were removed // when Win9x was shutting down; ignore these files // if (!DoesFileExist (FileTempLocation)) { if (!StringIMatch (GetFileNameFromPath (FileNtLocation), TEXT("desktop.ini"))) { DEBUGMSG ((DBG_ERROR, "MergeIniFile: File does not exist: %s (Nt=%s)", FileTempLocation, FileNtLocation)); return FALSE; } return TRUE; } if (!DoesFileExist (FileNtLocation)) { // // just copy back to the original file // if the file belongs to a directory that NT doesn't install, // create it now // StackStringCopy (TempPath, FileNtLocation); p = _tcsrchr (TempPath, TEXT('\\')); if (p) { *p = 0; if (GetFileAttributes (TempPath) == (DWORD)-1) { MakeSurePathExists (TempPath, TRUE); } } if (!CopyFile (FileTempLocation, FileNtLocation, FALSE)) { DEBUGMSG ((DBG_ERROR,"MergeIniFile: Cannot copy %s to %s", FileTempLocation, FileNtLocation)); return FALSE; } return TRUE; } if (!GetTempFileName (g_WinDir, TEXT("INI"), 0, TempPath)) { DEBUGMSG ((DBG_ERROR,"Merge Ini File : Cannot create a temporary file")); return FALSE; } __try { // // first of all we copy this INI file to be sure that GetPrivateProfileString function // does not map our requests into registry // if (!CopyFile (FileTempLocation, TempPath, FALSE)) { DEBUGMSG ((DBG_ERROR,"Merge Ini File : Cannot copy %s to %s", FileTempLocation, TempPath)); Result = FALSE; __leave; } Attribs = GetFileAttributes (FileNtLocation); SetFileAttributes (FileNtLocation, FILE_ATTRIBUTE_NORMAL); MYASSERT (Attribs != (DWORD)-1); // // now trying to get section buffer from the INI file // We will try to get section buffer in a 1024 bytes buffer. If this is not enough then // we will increase buffer size with 1024 and so on. // if (!pLoadIniFileBuffer (TempPath, NULL, NULL, &SectionBuf)) { DEBUGMSG ((DBG_ERROR,"Merge Ini File : Cannot load section buffer for %s",TempPath)); Result = FALSE; __leave; } fileName = GetFileNameFromPath (FileNtLocation); if (!fileName) { Result = FALSE; __leave; } __try { // // now we have all sections of the INI file and proceeding // Section = SectionBuf; // // there is a loop here for every section in the buffer // while (*Section) { // // now trying to get key buffer for this section // if (!pLoadIniFileBuffer (TempPath, Section, NULL, &KeyBuf)) { DEBUGMSG ((DBG_ERROR,"Merge Ini File : Cannot load key buffer for %s in %s", Section, TempPath)); Result = FALSE; continue; } __try { // // now we have all keys from this section and proceeding // Key = KeyBuf; // // there is a loop here for every key in the section // while (*Key) { BOOL unused; // // build the corresponding registry key // regKey = pMapIniSectionKeyToRegistryKey (fileName, Section, Key); if (pShouldSaveKey (fileName, Section, Key, regKey, &unused, FALSE, TRUE)) { GetPrivateProfileString( Section, Key, TEXT(""), srcValue, MEMDB_MAX, TempPath ); GetPrivateProfileString( Section, Key, TEXT(""), destValue, MEMDB_MAX, FileNtLocation ); if (*srcValue && !*destValue || TempHasPriority && !StringMatch (srcValue, destValue)) { IniFileChanged = TRUE; // writing the new value if (!WritePrivateProfileString ( Section, Key, srcValue, FileNtLocation )) { DEBUGMSG ((DBG_ERROR,"Merge Ini File : Cannot write line %s=%s in %s in %s", Key, srcValue, Section, FileNtLocation)); Result = FALSE; } } } ELSE_DEBUGMSG (( DBG_VERBOSE, "Merge Ini File : Suppressing key %s in section %s of %s", Key, Section, FileNtLocation )); FreeText (regKey); Key = GetEndOfString (Key) + 1; } } __finally { if (KeyBuf) { MemFree (g_hHeap, 0, KeyBuf); } } Section = GetEndOfString (Section) + 1; } } __finally { if (SectionBuf) { MemFree (g_hHeap, 0, SectionBuf); } } // // finally, if we made any changes then we will copy the INI file back // if (IniFileChanged) { // flushing the INI file WritePrivateProfileString ( NULL, NULL, NULL, FileNtLocation ); } } __finally { if (Attribs != (DWORD)-1) { SetFileAttributes (FileNtLocation, Attribs); } SetFileAttributes (TempPath, FILE_ATTRIBUTE_NORMAL); DeleteFile (TempPath); } return Result; }