MIGINF.C
#include "pch.h" 
 
#define MIGRATEINF              ".\\migrate.inf" 
#define INITIALBUFFERSIZE       1024 
#define MIGINF_NOCREATE         FALSE 
#define MIGINF_CREATE           TRUE 
 
 
typedef struct tagMIGOBJECT MIGOBJECT, *PMIGOBJECT; 
struct tagMIGOBJECT { 
 
    PSTR        Key; 
    PSTR        Value; 
     
    PMIGOBJECT  Next; 
}; 
 
typedef struct tagMIGSECTION MIGSECTION, * PMIGSECTION; 
struct tagMIGSECTION { 
 
    PSTR        Name; 
    PMIGOBJECT  Items; 
 
    PMIGSECTION Next; 
}; 
 
PMIGSECTION g_MigrationInf; 
POOLHANDLE  g_Pool = NULL; 
 
 
static 
PCSTR 
pGetTypeAsString ( 
    IN MIGTYPE Type 
    ) 
{ 
    // 
    // Note: Strings must be in the same order as the  
    // corresponding types in the MIGTYPE enumeration above. 
    // 
    static PCHAR typeStrings[] = { 
            "FIRST - Invalid", 
            "File", 
            "Path", 
            "Registry", 
            "Message - Invalid", 
            "LAST - Invalid" 
        }; 
 
    assert(Type > MIG_FIRSTTYPE && Type < MIG_LASTTYPE); 
 
    return typeStrings[Type]; 
} 
 
static 
PMIGSECTION  
pFindSection ( 
    IN PCSTR SectionString, 
    IN BOOL  CreateIfNotExist 
    ) 
{ 
    PMIGSECTION rSection; 
 
    // 
    // We assume that SectionString is not null. 
    // 
    assert(SectionString); 
 
    rSection = g_MigrationInf; 
 
    while (rSection && (_mbsicmp(rSection -> Name,SectionString) != 0)) { 
 
        // 
        // Continue looking. 
        // 
        rSection = rSection -> Next; 
    } 
         
    if (!rSection && CreateIfNotExist) { 
        // 
        // No section was found matching this name. Make a new section and add it  
        // to the list. 
        // 
        rSection = PoolMemGetMemory(g_Pool,sizeof(MIGSECTION)); 
        if (rSection) { 
 
            ZeroMemory(rSection,sizeof(MIGSECTION)); 
            rSection -> Name  = PoolMemDuplicateStringA(g_Pool,SectionString); 
            rSection -> Next  = g_MigrationInf; 
            g_MigrationInf    = rSection; 
 
            if (!rSection -> Name) { 
                // 
                // Something went wrong when we tried to duplicate the SectionString. 
                // NULL out the rSection so that the caller doesn't get back a  
                // malformed section object. 
                // 
                rSection = NULL; 
            } 
        } 
    } 
 
    return rSection; 
} 
 
static 
BOOL 
pPathIsInPath( 
    IN PCSTR    SubPath, 
    IN PCSTR    ParentPath 
    ) 
{ 
    DWORD parentLength; 
    BOOL  rInPath; 
 
    // 
    // This function assumes both parameters are non-NULL. 
    // 
    assert(SubPath); 
    assert(ParentPath); 
     
    parentLength = _mbslen(ParentPath); 
 
    // 
    // A path is considered "in" another path if the path is in the ParentPath 
    // or a subdirectory of it. 
    // 
    rInPath = !_mbsnicmp(SubPath,ParentPath,parentLength); 
 
    if (rInPath) { 
        rInPath = SubPath[parentLength] == 0 || SubPath[parentLength] == '\\'; 
    } 
 
    return rInPath; 
 
} 
 
static 
DWORD 
pGetMbsSize ( 
    IN  LPCSTR  String 
    ) 
{ 
    DWORD rLength; 
     
    rLength = (DWORD) _mbschr(String,0) - (DWORD) String + 1; 
 
    return rLength; 
 
} 
 
 
static 
LPSTR  
pEscapeString ( 
    IN  MIGTYPE Type, 
    OUT LPSTR   EscapedString,  
    IN  LPCSTR  String 
    ) 
 
{ 
    LPSTR   stringStart; 
    static  CHAR exclusions[] = "[]~,;%\""; 
    INT     currentChar; 
 
    // 
    // We assume that all parameters are valid. 
    // 
    assert(EscapedString && String); 
 
    stringStart = EscapedString; 
 
    while (*String)  { 
        currentChar = _mbsnextc (String); 
         
        if (Type == MIG_REGKEY) { 
             
            // 
            // Registry keys require more complex escaping than do normal INF strings. 
            // 
            if (!_ismbcprint (currentChar) || _mbschr (exclusions, currentChar)) { 
                 
                // 
                // Escape unprintable or excluded character 
                // 
                wsprintfA (EscapedString, "~%X~", currentChar); 
                EscapedString = _mbschr (EscapedString, 0); 
                String = _mbsinc (String); 
            } 
            else { 
                // 
                // Copy multibyte character 
                // 
                if (isleadbyte (*String)) { 
                    *EscapedString = *String; 
                    EscapedString++; 
                    String++; 
                } 
                 
                *EscapedString = *String; 
                EscapedString++; 
                String++; 
            } 
        } 
        else { 
 
            // 
            // Escaping is pretty simple for non-registry keys. All we do is double up 
            // quotes and percents. 
            // 
            if (*String == '\"' || *String == '%') { 
 
                *EscapedString = *String; 
                EscapedString++; 
            } 
             
            // 
            // Copy multibyte character 
            // 
            if (isleadbyte (*String)) { 
                *EscapedString = *String; 
                EscapedString++; 
                String++; 
            } 
             
            *EscapedString = *String; 
            EscapedString++; 
            String++; 
        } 
    } 
 
    // 
    // Ensure that returned string is NULL terminated. 
    // 
    *EscapedString = 0; 
 
    return stringStart; 
} 
 
 
static 
PSTR 
pGetValueString ( 
    IN MIGTYPE    ObjectType, 
    IN LPCSTR     StringOne, 
    IN LPCSTR     StringTwo 
    ) 
{ 
    static PSTR     buffer; 
    static DWORD    bufferSize; 
    DWORD           maxLength; 
    PSTR            bufferEnd; 
     
    // 
    // This function assumes that StringOne exists. 
    // 
    assert(StringOne); 
 
    if (ObjectType == MIG_REGKEY) { 
        // 
        // Size: size of both strings, plus the size of the quotes, plus the size of the brackets  
        // for the value, * 6. This is the maximum size one of these could grow to, if every  
        // character had to be escaped out. 
        // 
        maxLength = (pGetMbsSize(StringOne) + (StringTwo ? pGetMbsSize(StringTwo) + 2 : 0)) * 6 + 2; 
    } 
    else { 
        // 
        // Size: size of the string * 2 (The max size if every char was a '%' or '"' plus the quotes. 
        // 
        maxLength = pGetMbsSize(StringOne) * 2 + 2; 
    } 
 
    if (maxLength > bufferSize) { 
 
        // 
        // Initialize our buffer, or create a larger one. 
        // 
        bufferSize = (maxLength > INITIALBUFFERSIZE) ? maxLength : INITIALBUFFERSIZE; 
        buffer = PoolMemCreateStringA(g_Pool,bufferSize); 
    } 
 
    if (buffer != NULL) { 
         
        // 
        // Insert initial quote. 
        // 
        *buffer = '"'; 
  
        // 
        // Massage the string to ensure it is a valid INF file string. 
        // 
        pEscapeString(ObjectType,_mbsinc(buffer),StringOne); 
 
        // 
        // If this is a REGISTRY entry, then we also need to add the value part of the string,  
        // if one was specified (In StringTwo) 
        // 
 
        if (ObjectType == MIG_REGKEY && StringTwo) { 
 
            // 
            // Add the opening bracket. 
            // 
            bufferEnd = _mbschr(buffer,0); 
            *bufferEnd = '['; 
             
            // 
            // Add the value string in, again making sure the string is valid for an INF file. 
            // 
            pEscapeString(ObjectType,_mbsinc(bufferEnd),StringTwo); 
 
            // 
            // Now, add the closing braket. 
            // 
            bufferEnd = _mbschr(buffer,0); 
            *bufferEnd = ']'; 
 
            // 
            // Terminate the string. 
            // 
            bufferEnd = _mbsinc(bufferEnd); 
            *bufferEnd = 0; 
        } 
 
        // 
        // Add the final quote. 
        // 
        bufferEnd = _mbschr(buffer,0); 
        *bufferEnd = '"'; 
        bufferEnd = _mbsinc(bufferEnd); 
        *bufferEnd = 0; 
    } 
     
    return buffer; 
} 
 
static 
BOOL 
pCreateMigObject ( 
    IN MIGTYPE          ObjectType, 
    IN PCSTR            ParamOne, 
    IN PCSTR            ParamTwo, 
    IN PMIGSECTION      Section 
    ) 
{ 
    BOOL            rSuccess; 
    PMIGOBJECT      newObject = NULL; 
 
    // 
    // pCreateMigObject uses a set of hueristics to correctly assemble an object.  
    // These hueristics are based on the ObjectType and the contents of ParamTwo. 
    //  
    // ObjectType       ParamTwo      Key                   Value 
    // ------------------------------------------------------------------------- 
    // MIG_REGKEY       <any>         ParamOne[ParamTwo]    Registry 
    // <other>          NULL          ParamOne              <ObjectType As String> 
    // <other>          non-NULL      ParamOne              ParamTwo 
    // 
    // 
 
 
    if (Section) { 
 
        // 
        // First, create an object... 
        // 
        newObject = PoolMemGetMemory(g_Pool,sizeof(MIGOBJECT)); 
 
        if (newObject) { 
 
            if (ObjectType == MIG_REGKEY) { 
 
                newObject -> Key =  
                    PoolMemDuplicateStringA(g_Pool,pGetValueString(ObjectType,ParamOne,ParamTwo)); 
 
                newObject -> Value = PoolMemDuplicateStringA(g_Pool,pGetTypeAsString(ObjectType)); 
            } 
            else { 
                 
                newObject -> Key =  
                    PoolMemDuplicateStringA(g_Pool,pGetValueString(ObjectType,ParamOne,NULL)); 
 
                if (ParamTwo) { 
                    newObject -> Value = 
                        PoolMemDuplicateStringA(g_Pool,pGetValueString(ObjectType,ParamTwo,NULL)); 
                } 
                else { 
 
                    newObject -> Value =  
                        PoolMemDuplicateStringA(g_Pool,pGetTypeAsString(ObjectType)); 
                } 
            } 
        } 
    } 
 
 
    if (newObject && newObject -> Key && newObject -> Value) { 
 
        // 
        // The object has been successfully created. Link it into the section. 
        // 
        newObject -> Next = Section -> Items; 
        Section -> Items = newObject; 
        rSuccess = TRUE; 
    } 
    else { 
        rSuccess = FALSE; 
    } 
 
    return newObject && newObject -> Key && newObject -> Value; 
} 
 
 
static 
BOOL 
pWriteInfSectionToDisk ( 
    IN PMIGSECTION Section 
    ) 
{ 
    PMIGOBJECT curObject; 
    BOOL       rSuccess = TRUE; 
 
    if (Section) { 
 
        curObject = Section -> Items; 
 
        while (curObject && rSuccess) { 
 
            if (Section -> Name && curObject -> Key && curObject -> Value) { 
             
                rSuccess = WritePrivateProfileString( 
                    Section   -> Name, 
                    curObject -> Key,  
                    curObject -> Value, 
                    MIGRATEINF 
                    ); 
            } 
 
            curObject = curObject -> Next; 
        } 
    } 
    else { 
        rSuccess = FALSE; 
    } 
 
    return rSuccess; 
} 
 
 
static 
BOOL 
pBuildListFromSection ( 
    IN PCSTR    SectionString 
    ) 
{ 
    HINF            infHandle; 
    PMIGSECTION     section; 
    PMIGOBJECT      currentObject; 
    INFCONTEXT      ic; 
    DWORD           size; 
    BOOL            rSuccess = TRUE; 
 
    // 
    // This function assumes that Section is non-NULL. 
    // 
    assert(SectionString); 
 
    currentObject = NULL; 
     
    // 
    // First find the section specified. 
    // 
    section = pFindSection(SectionString,MIGINF_CREATE); 
 
    if (section) { 
         
        infHandle = SetupOpenInfFileA(MIGRATEINF,NULL,INF_STYLE_WIN4,NULL); 
         
        if (infHandle != INVALID_HANDLE_VALUE) { 
             
            if (SetupFindFirstLine(infHandle,SectionString,NULL,&ic)) { 
                 
                do { 
 
                    // 
                    // Create the object. 
                    // 
                    currentObject = (PMIGOBJECT) PoolMemGetMemory(g_Pool,sizeof(MIGOBJECT)); 
                     
                    if (!currentObject) { 
                        rSuccess = FALSE; 
                        break; 
                    } 
                     
                    // 
                    // Get the size of the string. 
                    // 
                    if (!SetupGetLineTextA(&ic,NULL,NULL,NULL,NULL,0,&size)) { 
                        rSuccess = FALSE; 
                        break; 
                    } 
                     
                    // 
                    // Create a string large enough. 
                    // 
                    currentObject -> Key = PoolMemCreateStringA(g_Pool,size); 
                     
                    if (!currentObject -> Key) { 
                        rSuccess = FALSE; 
                        break; 
                    } 
                     
                    // 
                    // Get the string. 
                    // 
                    if (!SetupGetLineTextA(&ic,NULL,NULL,NULL,currentObject -> Key,size,NULL)) { 
                        rSuccess = FALSE; 
                        break; 
                    } 
                     
                    // 
                    // Successfully retrieved the line. 
                    // 
                    currentObject -> Value  = (PSTR) pGetTypeAsString(MIG_FILE); 
                    currentObject -> Next   = section -> Items; 
                    section -> Items        = currentObject; 
                     
                } while(SetupFindNextLine(&ic,&ic)); 
                 
            } 
             
            SetupCloseInfFile(infHandle); 
        } 
    } 
    else { 
        rSuccess = FALSE; 
    } 
 
    return rSuccess; 
} 
 
 
BOOL 
WINAPI 
MigInf_Initialize( 
    VOID 
    ) 
{ 
 
    // 
    // First, initialize our pool and Zero out the structure. 
    // 
    g_Pool = PoolMemInitPool(); 
 
 
    if (g_Pool) { 
         
        // 
        // Now, read in the migration paths and excluded paths sections. 
        // 
        if (!pBuildListFromSection(SECTION_MIGRATIONPATHS) || 
            !pBuildListFromSection(SECTION_EXCLUDEDPATHS)) { 
            // 
            // Something went wrong (i.e. out of memory. Destroy and NULL our pool. 
            // 
            PoolMemDestroyPool(g_Pool); 
            g_Pool = NULL; 
        } 
    } 
 
    // 
    // If our memory pool initialized successfully, return TRUE. 
    // 
    return (g_Pool != NULL); 
 
} 
 
 
VOID 
WINAPI 
MigInf_CleanUp ( 
    VOID 
    ) 
{ 
    // 
    // Only thing we need to do is clean out pool mem. We'll NULL out the list header to make 
    // sure it isn't usable. 
    // 
    if (g_Pool) { 
        PoolMemDestroyPool(g_Pool); 
        g_Pool = NULL; 
    } 
     
    g_MigrationInf = NULL; 
 
} 
 
 
BOOL 
WINAPI 
MigInf_AddObject ( 
    IN MIGTYPE  ObjectType, 
    IN PCSTR    SectionString, 
    IN PCSTR    ParamOne, 
    IN PCSTR    ParamTwo 
    ) 
{ 
 
    return pCreateMigObject( 
        ObjectType, 
        ParamOne, 
        ParamTwo, 
        pFindSection(SectionString,MIGINF_CREATE) 
        ); 
} 
 
BOOL  
WINAPI  
MigInf_FirstInSection( 
    IN PCSTR SectionName,  
    OUT PMIGINFSECTIONENUM Enum 
    ) 
{ 
    PMIGSECTION section; 
 
    // 
    // We assume that Enum is valid. 
    // 
    assert(Enum); 
 
    section = pFindSection(SectionName,MIGINF_NOCREATE); 
 
    if (section) { 
        Enum -> EnumKey = (PVOID) section -> Items; 
    } 
 
    return MigInf_NextInSection(Enum); 
} 
 
BOOL  
WINAPI  
MigInf_NextInSection( 
    IN OUT PMIGINFSECTIONENUM Enum 
    ) 
{ 
 
 
    BOOL            rSuccess = FALSE; 
 
    // 
    // We assume that the Enum is valid. 
    // 
    assert(Enum); 
 
    if (Enum -> EnumKey) { 
 
        Enum -> Key     = ((PMIGOBJECT) (Enum -> EnumKey)) -> Key; 
        Enum -> Value   = ((PMIGOBJECT) (Enum -> EnumKey)) -> Value; 
        Enum -> EnumKey = ((PVOID) ((PMIGOBJECT) (Enum -> EnumKey)) -> Next); 
        rSuccess = TRUE; 
    } 
 
    return rSuccess; 
} 
 
 
BOOL 
WINAPI 
MigInf_WriteInfToDisk ( 
    VOID 
    ) 
{ 
 
    BOOL        rSuccess = TRUE; 
    PMIGSECTION curSection; 
     
    // 
    // Simply loop through all of the sections, writing each of them to disk. 
    // As long as WriteSectionToDisk works, we work. 
    // 
    curSection = g_MigrationInf; 
 
    while (curSection && rSuccess) { 
 
        // 
        // We skip the [Excluded Paths] and [Migration Paths] sections. 
        // 
        if (_mbsicmp(curSection -> Name,SECTION_EXCLUDEDPATHS) && 
            _mbsicmp(curSection -> Name,SECTION_MIGRATIONPATHS)) { 
             
            rSuccess = pWriteInfSectionToDisk(curSection); 
        }  
 
        curSection = curSection -> Next; 
         
    } 
 
    return rSuccess; 
} 
 
BOOL 
WINAPI 
MigInf_PathIsExcluded ( 
    IN PCSTR    Path 
    ) 
{ 
    PMIGOBJECT  curExcluded; 
    PMIGSECTION section; 
    BOOL        rIsExcluded = FALSE; 
 
    // 
    // We assume Path is valid. 
    // 
    assert(Path); 
     
    section = pFindSection(SECTION_EXCLUDEDPATHS,MIGINF_NOCREATE); 
 
    if (section) { 
 
        curExcluded = section -> Items; 
         
        while (curExcluded && !rIsExcluded) { 
             
            rIsExcluded = pPathIsInPath(Path,curExcluded -> Key); 
            curExcluded = curExcluded -> Next; 
        } 
    } 
     
    return rIsExcluded; 
} 
 
 
 
PCSTR 
WINAPI 
MigInf_GetNewSectionName ( 
    VOID 
    ) 
{ 
 
    static CHAR     sectionName[20]; 
    static DWORD    seedNum=0; 
 
 
    sprintf(sectionName,"msg%0.7u",seedNum++); 
 
    return sectionName; 
}