// // ParseInf.cpp // // Code that parses network INF files // // History: // // ?/??/1999 KenSh Created for JetNet // 9/29/1999 KenSh Repurposed for Home Networking Wizard // #include "stdafx.h" #include "ParseInf.h" #include "SortStr.h" #include "Registry.h" #define SECTION_BUFFER_SIZE (32 * 1024) // Non-localized strings #define SZ_INF_BACKUP_SUFFIX ".inf (HNW backup)" #define SZ_MODIFIED_INF_HEADER "; Modified by Home Networking Wizard\r\n" \ "; Original version backed up to \"" #define SZ_MODIFIED_INF_HEADER2 "\"\r\n" #define SZ_CHECK_MODIFIED_HEADER "; Modified by Home Networking Wizard" ////////////////////////////////////////////////////////////////////////////// // Utility functions int GetInfDirectory(LPTSTR pszBuf, int cchBuf, BOOL bAppendBackslash) { CRegistry regWindows; int cch = 0; if (regWindows.OpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", KEY_QUERY_VALUE)) { cch = regWindows.QueryStringValue("DevicePath", pszBuf, cchBuf); } // Fill in a default if the reg key is missing // REVIEW: Is this likely enough that we should even bother? if (cch == 0) { ASSERT(cchBuf > 8); cch = GetWindowsDirectory(pszBuf, cchBuf - 4); if (0!=cch) { if (pszBuf[cch-1] != '\\') pszBuf[cch++] = '\\'; } lstrcpy(pszBuf + cch, "INF"); cch += 3; } if (bAppendBackslash) { if (pszBuf[cch-1] != '\\') { pszBuf[cch++] = '\\'; pszBuf[cch] = '\0'; } } return cch; } int GetFullInfPath(LPCTSTR pszPartialPath, LPTSTR pszBuf, int cchBuf) { if (IsFullPath(pszPartialPath)) { lstrcpyn(pszBuf, pszPartialPath, cchBuf); } else { int cch = GetInfDirectory(pszBuf, cchBuf, TRUE); lstrcpyn(pszBuf + cch, pszPartialPath, cchBuf - cch); } return lstrlen(pszBuf); } int AddCommaSeparatedValues(const CStringArray& rgTokens, CStringArray& rgValues, BOOL bIgnoreInfSections) { int cAdded = 0; for (int iToken = 2; iToken < rgTokens.GetSize(); iToken++) { CString& strTok = ((CStringArray&)rgTokens).ElementAt(iToken); if (strTok.Compare(",") == 0) continue; if (strTok.Compare(";") == 0) break; // Hack: ignore sections whose name ends in ".inf" if (bIgnoreInfSections) { if (0 == lstrcmpi(FindExtension(strTok), "inf")) continue; } rgValues.Add(strTok); cAdded++; } return cAdded; } // Builds a list of all files that need to be copied for the device BOOL GetDeviceCopyFiles(CInfParser& parser, LPCTSTR pszDeviceID, CDriverFileArray& rgDriverFiles) { if (!parser.GotoSection("Manufacturer")) return FALSE; CStringArray rgMfr; CStringArray rgLineTokens; while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 3 && rgLineTokens.ElementAt(1).Compare("=") == 0) rgMfr.Add(rgLineTokens.ElementAt(2)); } CString strNdiSection; // Look in each manufacturer section (e.g. "[3COM]") for the given DeviceID for (int iMfr = 0; iMfr < rgMfr.GetSize(); iMfr++) { if (!parser.GotoSection(rgMfr[iMfr])) continue; while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 5 && rgLineTokens.ElementAt(1).Compare("=") == 0 && rgLineTokens.ElementAt(3).Compare(",") == 0) { if (rgLineTokens.ElementAt(4).CompareNoCase(pszDeviceID) == 0) { strNdiSection = rgLineTokens.ElementAt(2); break; } } } if (!strNdiSection.IsEmpty()) break; } if (strNdiSection.IsEmpty()) return FALSE; CStringArray rgCopySections; CStringArray rgAddRegSections; // Look in [DeviceID.ndi] section for AddReg= and CopyFiles= if (!parser.GotoSection(strNdiSection)) return FALSE; while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 3 && rgLineTokens.ElementAt(1).Compare("=") == 0) { CString& strKey = rgLineTokens.ElementAt(0); CString& strValue = rgLineTokens.ElementAt(2); if (strKey.CompareNoCase("AddReg") == 0) { AddCommaSeparatedValues(rgLineTokens, rgAddRegSections, FALSE); } else if (strKey.CompareNoCase("CopyFiles") == 0) { AddCommaSeparatedValues(rgLineTokens, rgCopySections, FALSE); } } } // Look through AddReg sections for HKR,Ndi\Install,,,"DeviceID.Install" for (int iAddReg = 0; iAddReg < rgAddRegSections.GetSize(); iAddReg++) { if (!parser.GotoSection(rgAddRegSections[iAddReg])) continue; while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 7 && rgLineTokens.ElementAt(0).CompareNoCase("HKR") == 0 && rgLineTokens.ElementAt(1).Compare(",") == 0 && rgLineTokens.ElementAt(2).CompareNoCase("Ndi\\Install") == 0 && rgLineTokens.ElementAt(3).Compare(",") == 0) { // Pull out the 5th comma-separated string, and pull the quotes off int iSection = 2; for (int iToken = 4; iToken < rgLineTokens.GetSize(); iToken++) { CString& strTok = rgLineTokens.ElementAt(iToken); if (strTok.Compare(";") == 0) break; if (strTok.Compare(",") == 0) { iSection++; continue; } if (iSection == 4) { CString strSection = strTok; if (strSection[0] == '\"') strSection = strSection.Mid(1, strSection.GetLength() - 2); rgCopySections.Add(strSection); break; } } } } } // Look in [DeviceID.Install], etc., sections for CopyFiles= lines for (int iCopyFiles = 0; iCopyFiles < rgCopySections.GetSize(); iCopyFiles++) { parser.GetFilesFromInstallSection(rgCopySections[iCopyFiles], rgDriverFiles); } parser.GetFilesFromCopyFilesSections(rgCopySections, rgDriverFiles); return TRUE; } BOOL GetDeviceCopyFiles(LPCTSTR pszInfFileName, LPCTSTR pszDeviceID, CDriverFileArray& rgDriverFiles) { CInfParser parser; if (!parser.LoadInfFile(pszInfFileName)) return FALSE; return GetDeviceCopyFiles(parser, pszDeviceID, rgDriverFiles); } CDriverFileArray::~CDriverFileArray() { for (int i = 0; i < GetSize(); i++) { free((DRIVER_FILE_INFO*)GetAt(i)); } } ////////////////////////////////////////////////////////////////////////////// // CInfParser CInfParser::CInfParser() { m_pszFileData = NULL; } CInfParser::~CInfParser() { free(m_pszFileData); } BOOL CInfParser::LoadInfFile(LPCTSTR pszInfFile, LPCTSTR pszSeparators) { TCHAR szInfFile[MAX_PATH]; GetFullInfPath(pszInfFile, szInfFile, _countof(szInfFile)); free(m_pszFileData); m_pszFileData = (LPSTR)LoadFile(szInfFile, &m_cbFile); m_iPos = 0; m_strSeparators = pszSeparators; m_strExtSeparators = pszSeparators; m_strExtSeparators += " \t\r\n"; m_strFileName = pszInfFile; return (BOOL)m_pszFileData; } BOOL CInfParser::Rewind() { ASSERT(m_pszFileData != NULL); m_iPos = 0; return (BOOL)m_pszFileData; } BOOL CInfParser::GotoNextLine() { ASSERT(m_pszFileData != NULL); for (LPTSTR pch = m_pszFileData + m_iPos; *pch != '\0' && *pch != '\r' && *pch != '\n'; pch++) NULL; if (*pch == '\r') pch++; if (*pch == '\n') pch++; DWORD iPos = (DWORD)(pch - m_pszFileData); if (iPos == m_iPos) return FALSE; // we were already at EOF m_iPos = iPos; return TRUE; } BOOL CInfParser::GetToken(CString& strTok) { strTok.Empty(); if (m_pszFileData == NULL) { ASSERT(FALSE); return FALSE; } LPTSTR pch = m_pszFileData + m_iPos; TCHAR ch; BOOL bQuoted = FALSE; // Skip whitespace while ((ch = *pch) == ' ' || ch == '\t') pch++; if (ch == '\0') goto done; // Check for linebreak if (ch == '\r' || ch == '\n') { strTok = ch; pch++; if (ch == '\r' && *pch == '\n') { strTok += '\n'; pch++; } goto done; } // Check for separator if (NULL != strchr(m_strSeparators, ch)) { strTok = ch; pch++; goto done; } LPTSTR pszStart; for (pszStart = pch; (ch = *pch) != '\0'; pch++) { if (!bQuoted && NULL != strchr(m_strExtSeparators, ch)) { break; } else if (ch == '\"') { bQuoted = !bQuoted; } } if (pch != pszStart) { DWORD cch = (DWORD)(pch - pszStart); LPTSTR pszToken = strTok.GetBufferSetLength(cch); lstrcpyn(pszToken, pszStart, cch+1); } done: m_iPos = (DWORD)(pch - m_pszFileData); return (BOOL)strTok.GetLength(); } BOOL CInfParser::GetLineTokens(CStringArray& sa) { CString strToken; BOOL bResult = FALSE; sa.RemoveAll(); while (GetToken(strToken)) { bResult = TRUE; // not at EOF if (strToken[0] == '\r' || strToken[0] == '\n') break; sa.Add(strToken); } return bResult; } BOOL CInfParser::GetSectionLineTokens(CStringArray& sa) { begin: if (!GetLineTokens(sa)) return FALSE; if (sa.GetSize() == 0) goto begin; CString& strFirst = sa.ElementAt(0); if (strFirst[0] == '[') // end of section return FALSE; if (strFirst.Compare(";") == 0) // comment { sa.RemoveAll(); goto begin; } return TRUE; } BOOL CInfParser::GotoSection(LPCTSTR pszSection) { CString strSection; CString strToken; if (!Rewind()) return FALSE; TCHAR ch; BOOL bStartOfLine = TRUE; int cchSection = lstrlen(pszSection) + 1; for (LPTSTR pch = m_pszFileData; (ch = *pch) != '\0'; pch++) { if (ch == '\r' || ch == '\n') { bStartOfLine = TRUE; } else if (bStartOfLine) { if (ch == '[') { LPTSTR pchCloseBracket = strchr(pch+1, ']'); if ((int)(pchCloseBracket - pch) == cchSection) { CString str(pch+1, cchSection-1); if (str.CompareNoCase(pszSection) == 0) { pch = pchCloseBracket+1; if (*pch == '\r') pch++; if (*pch == '\n') pch++; m_iPos = (DWORD)(pch - m_pszFileData); return TRUE; } } } bStartOfLine = FALSE; } } return FALSE; } int CInfParser::GetProfileInt(LPCTSTR pszSection, LPCTSTR pszKey, int nDefault) { DWORD iPos = m_iPos; if (GotoSection(pszSection)) { CStringArray rgTokens; while (GetSectionLineTokens(rgTokens)) { if (rgTokens.GetSize() >= 3 && rgTokens.ElementAt(0).CompareNoCase(pszKey) == 0&& rgTokens.ElementAt(1).Compare("=") == 0) { nDefault = MyAtoi(rgTokens.ElementAt(2)); break; } } } m_iPos = iPos; return nDefault; } BOOL CInfParser::GetFilesFromInstallSection(LPCTSTR pszSection, CDriverFileArray& rgAllFiles) { CStringArray rgLineTokens; CStringArray rgCopyFilesSections; if (!GotoSection(pszSection)) return FALSE; while (GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 3 && rgLineTokens.ElementAt(0).CompareNoCase("CopyFiles") == 0 && rgLineTokens.ElementAt(1).Compare("=") == 0) { AddCommaSeparatedValues(rgLineTokens, rgCopyFilesSections, FALSE); // REVIEW: There can be AddReg= lines here. Do we need to // check the referenced sections for more CopyFiles= lines? } } GetFilesFromCopyFilesSections(rgCopyFilesSections, rgAllFiles); return TRUE; } // Looks in [DestinationDirs] for pszSectionName, fills pbDirNumber and pszSubDir with // the matching target directory and (optional) subdirectory. BOOL CInfParser::GetDestinationDir(LPCTSTR pszSectionName, BYTE* pbDirNumber, LPTSTR pszSubDir, UINT cchSubDir) { DWORD iSavedPos = m_iPos; BOOL bSuccess = FALSE; *pbDirNumber = 0; pszSubDir[0] = '\0'; if (GotoSection("DestinationDirs")) { CStringArray rgTokens; while (GetSectionLineTokens(rgTokens)) { if (rgTokens.GetSize() >= 3 && rgTokens.ElementAt(0).CompareNoCase(pszSectionName) == 0 && rgTokens.ElementAt(1).Compare("=") == 0) { *pbDirNumber = (BYTE)MyAtoi(rgTokens.ElementAt(2)); if (rgTokens.GetSize() >= 5 && rgTokens.ElementAt(3).Compare(",") == 0) { lstrcpyn(pszSubDir, rgTokens.ElementAt(4), cchSubDir); } bSuccess = TRUE; break; } } } m_iPos = iSavedPos; return bSuccess; } void CInfParser::GetFilesFromCopyFilesSections(const CStringArray& rgCopyFiles, CDriverFileArray& rgAllFiles) { CStringArray rgLineTokens; for (int iSection = 0; iSection < rgCopyFiles.GetSize(); iSection++) { TCHAR szTargetSubDir[MAX_PATH]; BYTE nTargetDir; GetDestinationDir(rgCopyFiles[iSection], &nTargetDir, szTargetSubDir, _countof(szTargetSubDir)); // BYTE nTargetDir = (BYTE)GetProfileInt("DestinationDirs", rgCopyFiles[iSection], 0); #ifdef _DEBUG if (nTargetDir == 0) TRACE("Warning: CopyFiles section [%s] has no destination directory.\r\n", rgCopyFiles[iSection]); #endif if (!GotoSection(rgCopyFiles[iSection])) continue; // Get the first item from each line while (GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() == 1 || (rgLineTokens.GetSize() >= 2 && (rgLineTokens.ElementAt(1).Compare(",") == 0 || rgLineTokens.ElementAt(1).Compare(";") == 0))) { CString& strFileName = rgLineTokens.ElementAt(0); // Don't install this INF file // REVIEW: might want to allow this based on a flag or something if (0 != lstrcmpi(FindFileTitle(strFileName), FindFileTitle(m_strFileName))) { UINT cbFileInfo = sizeof(DRIVER_FILE_INFO) + strFileName.GetLength() + lstrlen(szTargetSubDir) + 1; DRIVER_FILE_INFO* pFileInfo = (DRIVER_FILE_INFO*)malloc(cbFileInfo); TRACE("Error: Malloc failed on DRIVER_FILE_INFO.\r\n"); if (pFileInfo) { pFileInfo->nTargetDir = nTargetDir; lstrcpy(pFileInfo->szFileTitle, strFileName); lstrcpy(pFileInfo->szFileTitle + strFileName.GetLength() + 1, szTargetSubDir); rgAllFiles.Add(pFileInfo); } } } } } // TODO: Remove duplicate files (maybe here, maybe not) } int CInfParser::GetNextSourceFile(LPTSTR pszBuf, BYTE* pDiskNumber) { LPTSTR pch = m_pszFileData + m_iPos; int cch = 0; BYTE bDiskNumber = 0; for (;;) { TCHAR ch; while ((ch = *pch) == '\r' || ch == '\n') pch++; if (ch == '\0' || ch == '[' || cch != 0) break; if (ch != ';') { LPTSTR pchStart = pch; while ((UCHAR)(ch = *pch) > 32 && ch != '=') pch++; cch = (int)(pch - pchStart); lstrcpyn(pszBuf, pchStart, cch+1); // skip whitespace while avoiding '\0' while ((UCHAR)(*pch-1) < 32) pch++; if (*pch == '=') { pch++; // skip whitespace while avoiding '\0' while ((UCHAR)(*pch-1) < 32) pch++; bDiskNumber = (BYTE)MyAtoi(pch); #if 1 // ignore files with disk number of 0 if (bDiskNumber == 0) cch = 0; #endif } } // skip text up to newline while ((ch = *pch) != '\0' && ch != '\r' && ch != '\n') pch++; } *pDiskNumber = bDiskNumber; m_iPos = (DWORD)(pch - m_pszFileData); return cch; } /* int CInfParser::ReadSourceFilesSection(INF_LAYOUT_FILE* prgFiles, int cFiles) { WORD wOffset = (WORD)(cFiles * sizeof(INF_LAYOUT_FILE)); LPTSTR pchDest = (LPTSTR)((LPBYTE)m_prgFiles + wOffset); INF_LAYOUT_FILE* pFile = prgFiles; INF_LAYOUT_FILE* pFileEnd = pFile + cFiles; while (pFile < pFileEnd) { pFile->wNameOffset = (WORD)((LPBYTE)pchDest - (LPBYTE)prgFiles); int cch = parser.GetNextSourceFile(pchDest, &pFile->iDisk); wOffset += cch+1; pchDest += cch+1; pFile++; } LPTSTR pch = m_pszFileData + m_iPos; int cch = 0; *pDiskNumber = 0; for (;;) { TCHAR ch; while ((ch = *pch) == '\r' || ch == '\n') pch++; if (ch == '\0' || ch == '[' || cch != 0) break; if (ch != ';') { LPTSTR pchStart = pch; while ((UCHAR)(ch = *pch) > 32 && ch != '=') pch++; cch = (int)(pch - pchStart); lstrcpyn(pszBuf, pchStart, cch+1); // skip whitespace while avoiding '\0' while ((UCHAR)(*pch-1) < 32) pch++; if (*pch == '=') { pch++; // skip whitespace while avoiding '\0' while ((UCHAR)(*pch-1) < 32) pch++; *pDiskNumber = (BYTE)atoi(pch); } } // skip text up to newline while ((ch = *pch) != '\0' && ch != '\r' && ch != '\n') pch++; } m_iPos = (DWORD)(pch - m_pszFileData); return cch; } */ void CInfParser::ScanSourceFileList(int* pcFiles, int* pcchAllFileNames) { int cFiles = 0; int cchAllFileNames = 0; LPTSTR pch = m_pszFileData + m_iPos; for (;;) { TCHAR ch; while ((ch = *pch) == '\r' || ch == '\n') pch++; if (ch == '\0' || ch == '[') break; if (ch != ';') { cFiles += 1; LPTSTR pchStart = pch; while ((UCHAR)(ch = *pch) >= 32 && ch != '=') pch++; cchAllFileNames += (int)(pch - pchStart); } // skip text up to newline while ((ch = *pch) != '\0' && ch != '\r' && ch != '\n') pch++; } *pcFiles = cFiles; *pcchAllFileNames = cchAllFileNames; } ////////////////////////////////////////////////////////////////////////////// // CInfLayoutFiles #define INF_LAYOUT_FILE_PADDING 40 // extra bytes at end of string data CInfLayoutFiles::CInfLayoutFiles() { m_prgFiles = NULL; m_pStringData = NULL; m_cFiles = 0; m_cbStringData = 0; #ifdef _DEBUG m_bSorted = FALSE; #endif } CInfLayoutFiles::~CInfLayoutFiles() { free(m_prgFiles); free(m_pStringData); for (int i = m_rgSourceDisks.GetSize()-1; i >= 0; i--) { delete m_rgSourceDisks[i]; } } LPTSTR CInfLayoutFiles::s_pStringData; int __cdecl CInfLayoutFiles::CompareInfLayoutFiles(const void* pEl1, const void* pEl2) { LPCTSTR psz1 = s_pStringData + ((INF_LAYOUT_FILE*)pEl1)->dwNameOffset; LPCTSTR psz2 = s_pStringData + ((INF_LAYOUT_FILE*)pEl2)->dwNameOffset; return lstrcmpi(psz1, psz2); } BOOL CInfLayoutFiles::Add(CInfParser& parser, BOOL bLayoutFile) { // Check if we've already added this layout file if (-1 != m_rgLayoutFileNames.Find(parser.m_strFileName)) return TRUE; BYTE iLayoutFile = (BYTE)m_rgLayoutFileNames.GetSize(); if (parser.GotoSection("SourceDisksFiles")) { DWORD dwTicks1 = GetTickCount(); int cFiles; int cchAllFileNames; parser.ScanSourceFileList(&cFiles, &cchAllFileNames); DWORD dwTicks2 = GetTickCount(); DWORD dwOffset = (DWORD)m_cbStringData; int iFile = m_cFiles; cchAllFileNames += m_cbStringData + cFiles; cFiles += m_cFiles; INF_LAYOUT_FILE* pFile = (INF_LAYOUT_FILE*)realloc(m_prgFiles, cFiles * sizeof(INF_LAYOUT_FILE)); LPTSTR pchDest = (LPTSTR)realloc(m_pStringData, cchAllFileNames + INF_LAYOUT_FILE_PADDING); if (pchDest) { m_pStringData = pchDest; } if (pFile) { m_prgFiles = pFile; } if (pchDest && pFile) { m_cFiles = cFiles; m_cbStringData = cchAllFileNames; pchDest += dwOffset; pFile += iFile; for ( ; iFile < m_cFiles; iFile++) { pFile->dwNameOffset = dwOffset; pFile->iLayout = iLayoutFile; int cch = parser.GetNextSourceFile(pchDest, &pFile->iDisk); dwOffset += cch+1; pchDest += cch+1; pFile++; } } else { return FALSE; } DWORD dwTicks3 = GetTickCount(); CString str; str.Format("LoadLayout(%s) timings: %d ms, %d ms. Total time: %d ms", parser.m_strFileName, dwTicks2-dwTicks1, dwTicks3-dwTicks2, dwTicks3-dwTicks1); TRACE("%s\r\n", str); // AfxMessageBox(str); // for (int i = 0; i < cFiles; i++) // { // INF_LAYOUT_FILE* pFile = &m_prgFiles[i]; // TRACE("File %d: %s=%d\r\n", i, m_pStringData + pFile->dwNameOffset), pFile->iDisk); // } #ifdef _DEBUG m_bSorted = FALSE; #endif } if (parser.GotoSection("SourceDisksNames")) { CStringArray rgTokens; while (parser.GetSectionLineTokens(rgTokens)) { if (rgTokens.GetSize() >= 3) { BYTE iDiskNumber = (BYTE)MyAtoi(rgTokens.ElementAt(0)); if (iDiskNumber != 0) { SOURCE_DISK_INFO* pDiskInfo = new SOURCE_DISK_INFO; pDiskInfo->wDiskID = MAKE_DISK_ID(iDiskNumber, iLayoutFile); // Get disk description, pull off quotes CString& strDesc = rgTokens.ElementAt(2); if (strDesc[0] == '\"') pDiskInfo->strDescription = strDesc.Mid(1, strDesc.GetLength()-2); else pDiskInfo->strDescription = strDesc; // If this is Layout*.inf, Get CAB filename, pull off quotes if (bLayoutFile && rgTokens.GetSize() >= 5) { CString& strCab = rgTokens.ElementAt(4); if (strCab[0] == '\"') pDiskInfo->strCabFile = strCab.Mid(1, strCab.GetLength()-2); else pDiskInfo->strCabFile = strCab; } m_rgSourceDisks.Add(pDiskInfo); } } } } // // Now add any referenced layout files // if (parser.GotoSection("version")) { CStringArray rgLayoutFiles; CStringArray rgLineTokens; while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 3 && rgLineTokens.ElementAt(0).CompareNoCase("LayoutFile") == 0 && rgLineTokens.ElementAt(1).Compare("=") == 0) { AddCommaSeparatedValues(rgLineTokens, rgLayoutFiles, FALSE); break; } } for (int i = 0; i < rgLayoutFiles.GetSize(); i++) { Add(rgLayoutFiles.ElementAt(i), TRUE); } } m_rgLayoutFileNames.Add(parser.m_strFileName, 0); return TRUE; } BOOL CInfLayoutFiles::Add(LPCTSTR pszInfFile, BOOL bLayoutFile) { CInfParser parser; if (!parser.LoadInfFile(pszInfFile)) return FALSE; return Add(parser, bLayoutFile); } void CInfLayoutFiles::Sort() { s_pStringData = m_pStringData; qsort(m_prgFiles, m_cFiles, sizeof(INF_LAYOUT_FILE), CompareInfLayoutFiles); #ifdef _DEBUG m_bSorted = TRUE; #endif } SOURCE_DISK_INFO* CInfLayoutFiles::FindDriverFileSourceDisk(LPCTSTR pszDriverFileTitle) { ASSERT(m_bSorted); // Build a dummy layout-file key to allow the standard binary search to work // (Note that we've left INF_LAYOUT_FILE_PADDING chars at the end of the string data) ASSERT(lstrlen(pszDriverFileTitle) + 1 < INF_LAYOUT_FILE_PADDING); INF_LAYOUT_FILE key; key.dwNameOffset = m_cbStringData; lstrcpy(m_pStringData + m_cbStringData, pszDriverFileTitle); s_pStringData = m_pStringData; INF_LAYOUT_FILE* pResult = (INF_LAYOUT_FILE*)bsearch( &key, m_prgFiles, m_cFiles, sizeof(INF_LAYOUT_FILE), CompareInfLayoutFiles); if (pResult == NULL) { ASSERT(FALSE); return NULL; } // REVIEW: Is it worth making this a binary search? WORD wDiskID = MAKE_DISK_ID(pResult->iDisk, pResult->iLayout); for (int iDisk = 0; iDisk < m_rgSourceDisks.GetSize(); iDisk++) { SOURCE_DISK_INFO* pDiskInfo = m_rgSourceDisks[iDisk]; if (pDiskInfo->wDiskID == wDiskID) return pDiskInfo; } ASSERT(FALSE); return NULL; } #ifdef _DEBUG void CInfLayoutFiles::Dump() { TRACE("CInfLayoutFiles (0x%08x)\r\n", (int)this); for (int i = 0; i < m_cFiles; i++) { INF_LAYOUT_FILE* pFile = &m_prgFiles[i]; TRACE(" File %d: %s, layout %d, disk %d\r\n", i, m_pStringData + pFile->dwNameOffset, (int)pFile->iLayout, (int)pFile->iDisk); } } #endif // _DEBUG ////////////////////////////////////////////////////////////////////////////// // CInfFileList CInfFileList::CInfFileList() { } CInfFileList::~CInfFileList() { for (int i = m_rgDriverFiles.GetSize()-1; i >= 0; i--) { free(m_rgDriverFiles[i]); } for (i = m_rgCabFiles.GetSize() - 1; i >= 0; i--) { free((LPTSTR)m_rgCabFiles.GetItemData(i)); } } void CInfFileList::SetDriverSourceDir(LPCTSTR pszSourceDir) { m_strDriverSourceDir = pszSourceDir; } BOOL CInfFileList::AddBaseFiles(LPCTSTR pszInfFile) { CInfParser parser; if (!parser.LoadInfFile(pszInfFile)) return FALSE; if (!parser.GotoSection("BaseWinOptions")) return FALSE; CStringArray rgSections; CStringArray rgLineTokens; while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 1) rgSections.Add(rgLineTokens.ElementAt(0)); } int cSections = rgSections.GetSize(); if (cSections == 0) return FALSE; // Walk through each major section, grabbing section names from CopyFiles= lines CStringArray rgCopyFiles; for (int iSection = 0; iSection < cSections; iSection++) { if (!parser.GotoSection(rgSections[iSection])) continue; // Look for "CopyFiles=" while (parser.GetSectionLineTokens(rgLineTokens)) { if (rgLineTokens.GetSize() >= 3 && rgLineTokens.ElementAt(0).CompareNoCase("CopyFiles") == 0 && rgLineTokens.ElementAt(1).Compare("=") == 0) { AddCommaSeparatedValues(rgLineTokens, rgCopyFiles, TRUE); } } } // Walk through each CopyFiles section, grabbing the names of files to copy parser.GetFilesFromCopyFilesSections(rgCopyFiles, m_rgDriverFiles); m_rgLayoutFiles.Add(parser); // for (int i = 0; i < rgAllFiles.GetSize(); i++) // { // TRACE("File %d: %s\r\n", i, rgAllFiles[i]); // } return TRUE; } BOOL CInfFileList::AddDeviceFiles(LPCTSTR pszInfFile, LPCTSTR pszDeviceID) { CInfParser parser; if (!parser.LoadInfFile(pszInfFile)) return FALSE; if (!GetDeviceCopyFiles(parser, pszDeviceID, m_rgDriverFiles)) return FALSE; // Build a list of all layout files, including current file m_rgLayoutFiles.Add(parser); return TRUE; } // Retrieves one of the standard setupx destination directories. // Always appends a backslash to the name. // Returns number of characters copied. // // See setupx.h for a list of valid LDID_ values. // int GetStandardTargetPath(int iDirNumber, LPCTSTR pszTargetSubDir, LPTSTR pszBuf) { int cch = GetWindowsDirectory(pszBuf, MAX_PATH); if (pszBuf[cch-1] != '\\') pszBuf[cch++] = '\\'; switch (iDirNumber) { case 10: // LDID_WIN break; case 11: // LDID_SYS cch = GetSystemDirectory(pszBuf, MAX_PATH); if (pszBuf[cch-1] != '\\') pszBuf[cch++] = '\\'; break; case 17: // LDID_INF lstrcpy(pszBuf + cch, "INF\\"); cch += 4; break; case 18: // LDID_HELP lstrcpy(pszBuf + cch, "HELP\\"); cch += 5; break; case 25: // LDID_SHARED (windows dir) break; case 26: // LDID_WINBOOT (windows dir) break; default: ASSERT(FALSE); cch = 0; break; } if (pszTargetSubDir != NULL && *pszTargetSubDir != '\0') { lstrcpy(pszBuf + cch, pszTargetSubDir); cch += lstrlen(pszTargetSubDir); if (pszBuf[cch-1] != '\\') pszBuf[cch++] = '\\'; } pszBuf[cch] = '\0'; return cch; } int GetDriverTargetPath(const DRIVER_FILE_INFO* pFileInfo, LPTSTR pszBuf) { LPCTSTR pszTargetSubDir = pFileInfo->szFileTitle + lstrlen(pFileInfo->szFileTitle) + 1; return GetStandardTargetPath(pFileInfo->nTargetDir, pszTargetSubDir, pszBuf); } // Returns number of CAB files (from Windows CD) that need to be copied. // Note that other files may still need to be copied from the driver source directory. int CInfFileList::BuildSourceFileList() { TCHAR szPath[MAX_PATH]; CSortedStringArray& rgCabFiles = m_rgCabFiles; CSortedStringArray& rgSourceFiles = m_rgSourceFiles; m_rgLayoutFiles.Sort(); #ifdef _DEBUG // m_rgLayoutFiles.Dump(); #endif for (int iDriverFile = 0; iDriverFile < m_rgDriverFiles.GetSize(); iDriverFile++) { DRIVER_FILE_INFO* pDriverFileInfo = m_rgDriverFiles[iDriverFile]; // Check if file is already installed // GetStandardTargetPath(pDriverFileInfo->nTargetDir, szPath); // MakePath(szPath, szPath, pDriverFileInfo->szFileTitle); // if (DoesFileExist(szPath)) // continue; // skip this file if (!m_strDriverSourceDir.IsEmpty()) { // Check if file exists in source directory MakePath(szPath, m_strDriverSourceDir, pDriverFileInfo->szFileTitle); if (DoesFileExist(szPath)) { if (-1 == rgSourceFiles.Find(pDriverFileInfo->szFileTitle)) { rgSourceFiles.Add(pDriverFileInfo->szFileTitle, 0); } continue; } } SOURCE_DISK_INFO* pSourceDiskInfo = m_rgLayoutFiles.FindDriverFileSourceDisk(pDriverFileInfo->szFileTitle); if (pSourceDiskInfo != NULL && !pSourceDiskInfo->strCabFile.IsEmpty()) { if (-1 == rgCabFiles.Find(pSourceDiskInfo->strCabFile)) { LPTSTR pszDiskName = lstrdup(pSourceDiskInfo->strDescription); rgCabFiles.Add(pSourceDiskInfo->strCabFile, (DWORD)pszDiskName); } continue; } } for (int i = 0; i < rgCabFiles.GetSize(); i++) { TRACE("%s\r\n", rgCabFiles[i]); } return rgCabFiles.GetSize(); } #if NOT_FINISHED BOOL CInfFileList::CheckWindowsCD(LPCTSTR pszDirectory) { if (m_rgCabFiles.GetSize() == 0) return TRUE; // no files to copy UINT uPrevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); // Check for existence of one of the cabs on the CD // REVIEW: if it's a fixed disk, should check for all files // REVIEW: if it's a network share, this could take a long time TCHAR szPath[MAX_PATH]; BOOL bResult = FALSE; MakePath(szPath, pszDirectory, "."); if (DoesFileExist(szPath)) { MakePath(szPath, pszDirectory, m_rgCabFiles[0]); bResult = DoesFileExist(szPath); if (!bResult) { // Search one level of subdirectories starting with "W" WIN32_FIND_DATA Find; HANDLE hFind; MakePath(szPath, pszDirectory, "W*.*"); if (INVALID_HANDLE_VALUE != (hFind = FindFirstFile(szPath, &Find))) { do { MakePath(szPath, pszDirectory, Find.cFileName); MakePath(szPath, szPath, m_rgCabFiles[0]); if (DoesFileExist(szPath)) { bResult = TRUE; break; } } while (FindNextFile(hFind, &Find)); FindClose(hFind); } } } SetErrorMode(uPrevErrorMode); return bResult; } BOOL CInfFileList::FindWindowsCD(HWND hwndParent) { TCHAR szPath[MAX_PATH]; // check the version of the Windows source path that we've saved if (theApp.GetProfileString(c_szRegVal_PrevSourcePath, szPath, _countof(szPath))) { if (CheckWindowsCD(szPath)) { // goto success; } } else { // Check the current Windows source path CRegistry reg; if (reg.OpenKey(HKEY_LOCAL_MACHINE, c_szSetupKey) && reg.QueryStringValue(c_szRegVal_SourcePath, szPath, _countof(szPath))) { if (CheckWindowsCD(szPath)) { goto success; } } } if (!PromptWindowsCD(hwndParent, szPath, szPath)) return FALSE; success: m_strWindowsCD = szPath; return TRUE; } BOOL CInfFileList::PromptWindowsCD(HWND hwndParent, LPCTSTR pszInitialDir, LPTSTR pszResultDir) { LPCTSTR pszDiskName = (LPCTSTR)m_rgCabFiles.GetItemData(0); CWinPathDlg dlg(pszInitialDir, pszDiskName, CWnd::FromHandle(hwndParent)); // Loop until user types a path to a valid CD, or clicks Cancel for (;;) { if (IDOK != dlg.DoModal()) return FALSE; if (CheckWindowsCD(dlg.m_strPath)) { lstrcpy(pszResultDir, dlg.m_strPath); return TRUE; } } } BOOL CInfFileList::CopySourceFiles(HWND hwndParent, LPCTSTR pszDestDir, PROGRESS_CALLBACK pfnProgress, LPVOID pvProgressParam) { int cFiles = m_rgCabFiles.GetSize() + m_rgSourceFiles.GetSize(); if (m_rgCabFiles.GetSize() > 0) { } return TRUE; } #endif ////////////////////////////////////////////////////////////////////////////// // Modify system INF class CInfUpdater : public CInfParser { public: CInfUpdater(LPCTSTR pszRequire = NULL, LPCTSTR pszExclude = NULL); ~CInfUpdater(); enum eUpdateType { update_NoVersionConflict = 0x01, update_NoCopyFiles = 0x02, update_RequireExclude = 0x04, }; BOOL IsModified(); BOOL UpdateInfFile(LPCTSTR pszBackupLocation, UINT updateType); LPCTSTR m_pszRequire; LPCTSTR m_pszExclude; protected: void WriteToCurPos(); void Write(LPCTSTR pszString); void Write(const void* pvData, UINT cbData); BOOL OpenTempFile(); BOOL CloseTempFile(); BOOL RenameTempFile(); protected: CString m_strTempFile; HANDLE m_hTempFile; BOOL m_bWriteSuccess; DWORD m_iWritePos; }; CInfUpdater::CInfUpdater(LPCTSTR pszRequire /*=NULL*/, LPCTSTR pszExclude /*=NULL*/) { m_hTempFile = INVALID_HANDLE_VALUE; m_pszRequire = pszRequire; m_pszExclude = pszExclude; } CInfUpdater::~CInfUpdater() { CloseTempFile(); if (!m_strTempFile.IsEmpty()) { DeleteFile(m_strTempFile); } } BOOL CInfUpdater::IsModified() { ASSERT(m_pszFileData != NULL); return !memcmp(SZ_CHECK_MODIFIED_HEADER, m_pszFileData, _lengthof(SZ_CHECK_MODIFIED_HEADER)); } BOOL CInfUpdater::UpdateInfFile(LPCTSTR pszBackupLocation, UINT updateType) { ASSERT(m_pszFileData != NULL); if (m_pszFileData == NULL) return FALSE; Rewind(); if (!OpenTempFile()) return FALSE; Write(SZ_MODIFIED_INF_HEADER); Write(pszBackupLocation); Write(SZ_MODIFIED_INF_HEADER2); BOOL bInSection = FALSE; for (;;) { // Skip whitespace while (m_pszFileData[m_iPos] == ' ' || m_pszFileData[m_iPos] == '\t') m_iPos++; // Note: we're always at the beginning of a line here. TCHAR ch = m_pszFileData[m_iPos]; if (ch == '\0') break; if (updateType & update_NoVersionConflict) { if (ch != '[' && ch != ';' && (UINT)ch > ' ') { // Look for lines w/ just a filename, and append ",,,32" to them LPTSTR pszLine = m_pszFileData + m_iPos; LPTSTR pchEnd = pszLine; while (*pchEnd != '\0' && *pchEnd != '\r' && *pchEnd != '\n' && *pchEnd != ';' && *pchEnd != '=' && *pchEnd != ',') { pchEnd++; } // don't change lines that already have ,,,16 or something // also don't change lines like CatalogFile=nettrans.cat if (*pchEnd != ',' && *pchEnd != '=') { // Backup over whitespace while (*(pchEnd-1) == ' ' || *(pchEnd-1) == '\t') pchEnd--; // Is it a filename? Note that some filenames will be missed here, but // we really only care about .dll, .386, .vxd, and a few others CString str(pszLine, (int)(pchEnd - pszLine)); if (lstrlen(FindExtension(str)) == 3) { m_iPos = (DWORD)(pchEnd - m_pszFileData); WriteToCurPos(); Write(",,,32"); } } } } if (updateType & update_NoCopyFiles) { if (0 == memcmp(m_pszFileData + m_iPos, "CopyFiles", _lengthof("CopyFiles"))) { // Comment out the reference to the CopyFiles section WriteToCurPos(); Write(";hc "); } } if (updateType & update_RequireExclude) { ASSERT(m_pszRequire != NULL); ASSERT(m_pszExclude != NULL); const TCHAR c_szRequireAll[] = _T("HKR,Ndi\\Compatibility,RequireAll,,\""); const TCHAR c_szExcludeAll[] = _T("HKR,Ndi\\Compatibility,ExcludeAll,,\""); LPCTSTR pszInsert = NULL; if (0 == memcmp(m_pszFileData + m_iPos, c_szRequireAll, _lengthof(c_szRequireAll))) pszInsert = m_pszRequire; else if (0 == memcmp(m_pszFileData + m_iPos, c_szExcludeAll, _lengthof(c_szExcludeAll))) pszInsert = m_pszExclude; if (pszInsert != NULL) { // Insert the appropriate string between the double-quotes ASSERT(_lengthof(c_szRequireAll) == _lengthof(c_szExcludeAll)); m_iPos += _lengthof(c_szRequireAll); WriteToCurPos(); Write(pszInsert); // Skip to closing quote while (m_pszFileData[m_iPos] != '\"' && m_pszFileData[m_iPos] != '\0') m_iPos += 1; m_iWritePos = m_iPos; } } GotoNextLine(); } WriteToCurPos(); if (!CloseTempFile()) return FALSE; if (!RenameTempFile()) return FALSE; return TRUE; } void CInfUpdater::WriteToCurPos() { ASSERT(m_iPos >= m_iWritePos); Write(m_pszFileData + m_iWritePos, m_iPos - m_iWritePos); m_iWritePos = m_iPos; } void CInfUpdater::Write(LPCTSTR pszString) { Write(pszString, lstrlen(pszString)); } void CInfUpdater::Write(const void* pvData, UINT cbData) { DWORD cbWritten; if (!WriteFile(m_hTempFile, pvData, cbData, &cbWritten, NULL) || cbData != cbWritten) m_bWriteSuccess = FALSE; } BOOL CInfUpdater::OpenTempFile() { TCHAR szDirectory[MAX_PATH]; GetFullInfPath(m_strFileName, szDirectory, _countof(szDirectory)); *(FindFileTitle(szDirectory)) = '\0'; LPTSTR pszBuf = m_strTempFile.GetBuffer(MAX_PATH); GetTempFileName(szDirectory, "inf", 0, pszBuf); m_strTempFile.ReleaseBuffer(); ASSERT(m_hTempFile == INVALID_HANDLE_VALUE); m_hTempFile = CreateFile(m_strTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (m_hTempFile == INVALID_HANDLE_VALUE) return FALSE; m_iWritePos = 0; m_bWriteSuccess = TRUE; return TRUE; } BOOL CInfUpdater::CloseTempFile() { if (m_hTempFile == INVALID_HANDLE_VALUE) return FALSE; BOOL bResult = CloseHandle(m_hTempFile); m_hTempFile = INVALID_HANDLE_VALUE; if (m_bWriteSuccess) m_bWriteSuccess = bResult; return m_bWriteSuccess; } BOOL CInfUpdater::RenameTempFile() { TCHAR szInfPath[MAX_PATH]; GetFullInfPath(m_strFileName, szInfPath, _countof(szInfPath)); if (!DeleteFile(szInfPath)) return FALSE; if (!MoveFile(m_strTempFile, szInfPath)) return FALSE; return TRUE; } // Modifies the given INF file to avoid the Version Conflict dialog. // Backs up original version in same directory, e.g. "Net (HomeClick backup).inf" BOOL ModifyInf_Helper(LPCTSTR pszInfFile, UINT updateType, LPCTSTR pszRequire = NULL, LPCTSTR pszExclude = NULL) { CInfUpdater infUpdate(pszRequire, pszExclude); if (!infUpdate.LoadInfFile(pszInfFile)) return FALSE; // Already updated? if (infUpdate.IsModified()) return FALSE; // already modified, don't modify again TCHAR szInfPath[MAX_PATH]; GetFullInfPath(pszInfFile, szInfPath, _countof(szInfPath)); TCHAR szBackup[MAX_PATH]; lstrcpy(szBackup, szInfPath); lstrcpy(FindExtension(szBackup)-1, SZ_INF_BACKUP_SUFFIX); FILETIME ftSrcCreated; FILETIME ftSrcModified; HANDLE hFile = CreateFile(szInfPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); GetFileTime(hFile, &ftSrcCreated, NULL, &ftSrcModified); CloseHandle(hFile); if (!CopyFile(szInfPath, szBackup, FALSE)) return FALSE; BOOL bResult = infUpdate.UpdateInfFile(szBackup, updateType); hFile = CreateFile(szInfPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); DWORD dwFileSize = GetFileSize(hFile, NULL); SetFileTime(hFile, &ftSrcCreated, NULL, &ftSrcModified); CloseHandle(hFile); return bResult; } BOOL ModifyInf_NoVersionConflict(LPCTSTR pszInfFile) { return ModifyInf_Helper(pszInfFile, CInfUpdater::update_NoVersionConflict); } BOOL ModifyInf_NoCopyFiles(LPCTSTR pszInfFile) { return ModifyInf_Helper(pszInfFile, CInfUpdater::update_NoCopyFiles); } BOOL ModifyInf_RequireExclude(LPCTSTR pszInfFile, LPCTSTR pszRequire, LPCTSTR pszExclude) { return ModifyInf_Helper(pszInfFile, CInfUpdater::update_RequireExclude, pszRequire, pszExclude); } BOOL ModifyInf_NoCopyAndRequireExclude(LPCTSTR pszInfFile, LPCTSTR pszRequire, LPCTSTR pszExclude) { return ModifyInf_Helper(pszInfFile, CInfUpdater::update_NoCopyFiles | CInfUpdater::update_RequireExclude, pszRequire, pszExclude); } BOOL RestoreInfBackup(LPCTSTR pszInfFile) { TCHAR szInfPath[MAX_PATH]; GetFullInfPath(pszInfFile, szInfPath, _countof(szInfPath)); TCHAR szBackup[MAX_PATH]; lstrcpy(szBackup, szInfPath); lstrcpy(FindExtension(szBackup)-1, SZ_INF_BACKUP_SUFFIX); /* FILETIME ftSrcCreated; FILETIME ftSrcModified; HANDLE hFile = CreateFile(szInfPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); GetFileTime(hFile, &ftSrcCreated, NULL, &ftSrcModified); CloseHandle(hFile); */ if (!DoesFileExist(szBackup)) return FALSE; if (!DeleteFile(szInfPath)) return FALSE; if (!MoveFile(szBackup, szInfPath)) return FALSE; /* hFile = CreateFile(szInfPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); GetFileTime(hFile, &ftSrcCreated, NULL, &ftSrcModified); CloseHandle(hFile); */ return TRUE; } BOOL CheckInfSectionInstallation(LPCTSTR pszInfFile, LPCTSTR pszInfSection) { CInfParser parser; if (!parser.LoadInfFile(pszInfFile)) { ASSERT(FALSE); return TRUE; // all known files are present even though it's an error } CDriverFileArray rgFiles; if (!parser.GetFilesFromInstallSection(pszInfSection, rgFiles)) { ASSERT(FALSE); return TRUE; // all known files are present even though it's an error } TCHAR szPath[MAX_PATH]; int cFiles = rgFiles.GetSize(); for (int iFile = 0; iFile < cFiles; iFile++) { int cch = GetDriverTargetPath(rgFiles[iFile], szPath); if (cch != 0) { lstrcpy(szPath + cch, rgFiles[iFile]->szFileTitle); if (!DoesFileExist(szPath)) return FALSE; } } return TRUE; } BOOL InstallInfSection(LPCTSTR pszInfFile, LPCTSTR pszInfSection, BOOL bWait) { // Make a modified copy of the INF BOOL bModifiedInf = ModifyInf_NoVersionConflict(pszInfFile); TCHAR szPath[MAX_PATH + 200]; int cch = GetWindowsDirectory(szPath, _countof(szPath)); #ifdef UNICODE wnsprintf(szPath + cch, ARRAYSIZE(szPath) - cch, L"\\RunDll.exe setupx.dll,InstallHinfSection %s 0 %s", pszInfSection, pszInfFile); #else wsprintf(szPath + cch, "\\RunDll.exe setupx.dll,InstallHinfSection %s 0 %s", pszInfSection, pszInfFile); #endif STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; BOOL bResult = CreateProcess(NULL, szPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); if (bResult) { if (bWait) { WaitForSingleObject(pi.hProcess, INFINITE); } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } if (bModifiedInf) { RestoreInfBackup(pszInfFile); } return bResult; }