|
|
/*++
Copyright (c) 2002 Microsoft Corporation
Module Name :
parseini.cpp
Abstract:
Class used to parse the ini file
Author:
Christopher Achille (cachille)
Project:
URLScan Update
Revision History: March 2002: Created
--*/
#include "stdafx.h"
#include "windows.h"
#include "parseini.h"
#include "stdio.h"
#include "malloc.h"
// Constructor
//
CIniFileLine::CIniFileLine() { m_szLine[0] = '\0'; m_szStrippedLineContents[0] = '\0'; }
// CopyLine
//
// Copy the specific line into both our buffers
//
BOOL CIniFileLine::CopyLine(LPWSTR szNewLineContents) { if ( _tcslen( szNewLineContents ) >= ( MAX_PATH - 1 ) ) { // Right now we only support loading Lines of MAX_PATH
// chars or less
return FALSE; }
// Copy String into both buffers
_tcscpy(m_szLine, szNewLineContents); _tcscpy(m_szStrippedLineContents, szNewLineContents);
// Strip off \r\n
StripOffEOL( m_szLine ); StripOffEOL( m_szStrippedLineContents );
// Strip off Comments
StripOffComments( m_szStrippedLineContents );
// Stipp off trailing white
StripOffTrailingWhite( m_szStrippedLineContents );
return TRUE; }
// CopyLine
//
// Copy the specific line into both our buffers
//
BOOL CIniFileLine::CopyLine(LPSTR szNewLineContents) { WCHAR szBuffer[MAX_PATH];
if ( !MultiByteToWideChar( CP_ACP, //CP_THREAD_ACP doesn't work on NT4
MB_ERR_INVALID_CHARS, szNewLineContents, // Input String
-1, // Null terminated
szBuffer, MAX_PATH ) ) { return FALSE; }
return CopyLine( szBuffer ); }
// StripOffEOL
//
// Strip off the EOL markers
//
void CIniFileLine::StripOffEOL(LPTSTR szString) { LPTSTR szCurrent;
szCurrent = _tcsstr( szString, L"\r" );
if ( szCurrent ) { *szCurrent = '\0'; return; }
szCurrent = _tcsstr( szString, L"\n" );
if ( szCurrent ) { // we won't hit this case, unless it was terminated with
// just \n
*szCurrent = '\0'; } }
// StripOffComments
//
// Strip off any comments that are in the line
//
void CIniFileLine::StripOffComments(LPTSTR szString) { LPTSTR szCurrent;
szCurrent = _tcsstr( szString, L";" );
if ( szCurrent ) { *szCurrent = '\0'; } }
// Strip Off TrailingWhite
//
// strip off all the trailing while stuff in the line
//
void CIniFileLine::StripOffTrailingWhite(LPTSTR szString) { // Set szCurrent at Null Terminator
LPTSTR szCurrent = szString + _tcslen( szString );
if ( ( *szCurrent == '\0' ) && ( szCurrent != szString ) ) { // Backup one character to start on the last char
szCurrent--; }
while ( ( szCurrent != szString ) && ( ( *szCurrent == ' ' ) || ( *szCurrent == '\t' ) ) ) { szCurrent--; }
if ( *szCurrent != '\0' ) { *( szCurrent + 1 ) = '\0'; } }
// QueryLine
//
// Query the contents of the line, before formatting
//
LPTSTR CIniFileLine::QueryLine() { return m_szLine; }
// QueryStrippedLine
//
// Query the line that has the comments, EOL, and
// white spaces removed
//
LPTSTR CIniFileLine::QueryStrippedLine() { return m_szStrippedLineContents; }
// Constructor
//
CIniFile::CIniFile() { // Initialize everything to empty
m_pIniLines = NULL; m_dwNumberofLines = 0; m_dwLinesAllocated = 0; m_dwCurrentLine = 0; m_bUnicodeFile = FALSE; }
// Destructor
//
CIniFile::~CIniFile() { ClearIni(); }
// ClearIni
//
// Clear all of the data in the ini file class
//
void CIniFile::ClearIni() { DWORD dwCurrent;
if ( m_pIniLines ) { for ( dwCurrent = 0; dwCurrent < m_dwLinesAllocated; dwCurrent ++ ) { if ( m_pIniLines[dwCurrent] ) { // Free memory, and reset to NULL
delete m_pIniLines[dwCurrent]; m_pIniLines[dwCurrent] = NULL; } }
m_dwNumberofLines = 0;
// Free Ini Table Altogether
delete m_pIniLines; m_pIniLines = NULL;
m_dwLinesAllocated = 0; }
}
// CreateMoreLines
//
// Create Room for more lines on the array. This allows us to
// stretch the array
BOOL CIniFile::CreateRoomForMoreLines() { CIniFileLine **pNewLines = NULL; DWORD dwNewNumberofLinesAllocated = m_dwLinesAllocated ? m_dwLinesAllocated * 2 : INIFILE_INITIALNUMBEROFLINES; DWORD dwCurrent;
// Enlarge buffer
pNewLines = (CIniFileLine **) realloc( m_pIniLines, sizeof( CIniFileLine * ) * dwNewNumberofLinesAllocated );
if ( pNewLines == NULL ) { // Failure to enlarge.
// Do not need to worry about old memory, since it is still valid in m_pIniLines
return FALSE; }
for ( dwCurrent = m_dwLinesAllocated; dwCurrent < dwNewNumberofLinesAllocated; dwCurrent++ ) { // Initialize to Null
pNewLines[ dwCurrent ] = NULL; }
m_pIniLines = pNewLines; m_dwLinesAllocated = dwNewNumberofLinesAllocated;
return TRUE; }
// AddLine
//
// Add a line at a specific location
//
// Parameters:
// dwLineNumber - The line number where it should be inserted
//
// Return
// NULL - Failure
// Pointer - A pointer to the CIniFileLine for the new line
//
CIniFileLine * CIniFile::AddLine( DWORD dwLocation ) { CIniFileLine *pNewLine; DWORD dwCurrentLine;
if ( dwLocation > m_dwNumberofLines ) { // Fail if we try to insert in a number to big
return FALSE; }
if ( ( m_dwNumberofLines == m_dwLinesAllocated ) && !CreateRoomForMoreLines() ) { // Could not create new lines for us
return FALSE; }
pNewLine = new (CIniFileLine);
if ( !pNewLine ) { // Failure to create new line
return FALSE; }
// Move all the lines after it down, to make room for this one
if ( m_dwNumberofLines != 0 ) { for ( dwCurrentLine = m_dwNumberofLines - 1; dwCurrentLine >= dwLocation; dwCurrentLine-- ) { m_pIniLines[ dwCurrentLine + 1 ] = m_pIniLines[ dwCurrentLine ]; } }
m_pIniLines[ dwLocation ] = pNewLine; m_dwNumberofLines++;
return pNewLine; }
// FindSectionNumber
//
// Find a section by a specific name
//
// Parameters
// szSectionName - [in] Section to look for
// pdwSection - [out] The Line #
//
// Return:
// TRUE - Found
// FALSE - Not found
//
BOOL CIniFile::FindSectionNumber(LPTSTR szSectionName, DWORD *pdwSection) { DWORD dwCurrentIndex;
// ASSERT( pdwSection );
for ( dwCurrentIndex = 0; dwCurrentIndex < m_dwNumberofLines; dwCurrentIndex++ ) { if ( IsSameSection( szSectionName, m_pIniLines[dwCurrentIndex]->QueryStrippedLine() ) ) { *pdwSection = dwCurrentIndex; return TRUE; } }
return FALSE; }
// IsSameSection
//
// Returns if the section is the same of not
//
// Parameters
// szSectionName - [in] The name of the Section (ie. TestSection)
// szLines - [in] The line in the file (ie. [TheSection])
//
// Return Values:
// TRUE - The same
// FALSE - Not the same
//
BOOL CIniFile::IsSameSection(LPTSTR szSectionName, LPTSTR szLine) { LPTSTR szSectionCurrent; LPTSTR szLineCurrent;
if ( szLine[0] != '[' ) { // If the first character is not a '[', then
// it is not een a section
return FALSE; }
szSectionCurrent = szSectionName; szLineCurrent = szLine + 1; // Skip preceding '['
while ( ( towlower( *szSectionCurrent ) == towlower( *szLineCurrent ) ) && ( *szSectionCurrent != '\0' ) ) { szSectionCurrent++; szLineCurrent++; }
if ( ( *szSectionCurrent == '\0' ) && ( *szLineCurrent == ']' ) && ( *(szLineCurrent + 1) == '\0' ) ) { // They match
return TRUE; }
return FALSE; }
// function: IsSameItem
//
// Is the Item Passed in, and the line, the same?
// Since items have nothing else on the line, this should
// be a simple _tcsicmp
//
// Parameters:
// szItemName - [in] The Item Name
// szLine - [in] The Line that is passed in
//
BOOL CIniFile::IsSameItem(LPTSTR szItemName, LPTSTR szLine) { return _tcsicmp( szItemName, szLine) == 0; }
// function: IsSameSetting
//
// Is the setting that is passed in, the same on
// that is on this specific line
//
// Parameters:
// szSettingName - [in] The Name of the setting (ie. MaxPath)
// szLine - [in] The Line to comapre (ie. MaxPath=30)
//
// Return Values:
// TRUE == same
// FALSE == no same
BOOL CIniFile::IsSameSetting(LPTSTR szSettingName, LPTSTR szLine) { LPTSTR szSettingCurrent; LPTSTR szLineCurrent;
szSettingCurrent = szSettingName; szLineCurrent = szLine;
while ( ( towlower( *szSettingCurrent ) == towlower( *szLineCurrent ) ) && ( *szSettingCurrent != '\0' ) ) { szSettingCurrent++; szLineCurrent++; }
if ( ( *szSettingCurrent == '\0' ) && ( *szLineCurrent == '=' ) ) { // They match
return TRUE; }
return FALSE; }
// DoesSectionExist
//
// Does the specified section exist?
//
BOOL CIniFile::DoesSectionExist(LPTSTR szSectionName) { DWORD dwLineNumber;
return FindSectionNumber( szSectionName, &dwLineNumber ); }
// GetLine
//
// Return a pointer to a particular line number
//
CIniFileLine * CIniFile::GetLine(DWORD dwLineNumber) { return m_pIniLines[ dwLineNumber ]; }
// FindSection
//
// Find a specific section, and set the current pointer to
// that section, then user FindNextLineInSection to parse
// through that section
//
// Parameters
// szSectionName - [in] The Name of the Section
//
BOOL CIniFile::FindSection( LPTSTR szSectionName ) { if ( !FindSectionNumber( szSectionName, &m_dwCurrentLine ) ) { // Could not find section
return FALSE; }
return TRUE; }
// FindNextLineInSection
//
// Find the next Line in the section, if any are left
//
BOOL CIniFile::FindNextLineInSection( CIniFileLine **ppCurrentLine ) { // Increment Line number
m_dwCurrentLine++;
if ( ( m_dwCurrentLine >= m_dwLinesAllocated ) || ( GetLine( m_dwCurrentLine ) == NULL ) ) { // We have either gone past the last line,
// or it does not exist, so return FALSE
return FALSE; }
if ( GetLine( m_dwCurrentLine )->QueryStrippedLine()[0] == '[' ) { // We have hit the begining of another section, so exit
return FALSE; }
*ppCurrentLine = GetLine( m_dwCurrentLine ); return TRUE; }
// DoesItemInSectionExist
//
// Does an Item in a particular section exist
//
// Parameters:
// szSectionName - [in] The section to search
// szItem - [in] The Item to find
//
// Return Values:
// TRUE - It exists
// FALSE - It does not exist
//
BOOL CIniFile::DoesItemInSectionExist(LPTSTR szSectionName, LPTSTR szItem) { CIniFileLine *pCurrentLine;
if ( !FindSection( szSectionName ) ) { // Could not find section, forget about the item
return FALSE; }
while ( FindNextLineInSection( &pCurrentLine ) ) { if ( IsSameItem( szItem, pCurrentLine->QueryStrippedLine() ) ) { // We have found the item
return TRUE; } }
return FALSE; }
// DoesSettingInSectionExist
//
// Does an Setting in a particular section exist
//
// Parameters:
// szSectionName - [in] The section to search
// szSetting - [in] The Setting to find
//
// Return Values:
// TRUE - It exists
// FALSE - It does not exist
//
BOOL CIniFile::DoesSettingInSectionExist(LPTSTR szSectionName, LPTSTR szSetting) { CIniFileLine *pCurrentLine;
if ( !FindSection( szSectionName ) ) { // Could not find section, forget about the item
return FALSE; }
while ( FindNextLineInSection( &pCurrentLine ) ) { if ( IsSameSetting( szSetting, pCurrentLine->QueryStrippedLine() ) ) { // We have found the item
return TRUE; } }
return FALSE; }
// GetNumberofLines
//
// Return the number of lines in the ini file
//
DWORD CIniFile::GetNumberofLines() { return m_dwNumberofLines; }
// AddSection
//
// Add a Section to the File
// (it inserts at end)
//
BOOL CIniFile::AddSection(LPTSTR szNewSectionName) { TCHAR szBuffer[MAX_PATH]; CIniFileLine *pNewLine;
if ( _tcslen( szNewSectionName ) >= ( MAX_PATH - 3 ) ) // Leave room for [,],\0
{ return FALSE; }
// Add a new line at the end
pNewLine = AddLine( GetNumberofLines() );
if ( !pNewLine ) { // Failed to Add
return FALSE; }
_tcscpy(szBuffer, L"["); _tcscat(szBuffer, szNewSectionName); _tcscat(szBuffer, L"]");
if ( !pNewLine->CopyLine( szBuffer ) ) { // Failed to copy data
return FALSE; }
// It has now been added, and updated with text
return TRUE; }
// SetStartforSectionIterator
//
// Set the current line for the Section Iterator,
// this is so that you can start at anyline in the file you want
// and iterate through that section
//
BOOL CIniFile::SetStartforSectionIterator( DWORD dwIndex ) { if ( dwIndex >= m_dwNumberofLines ) { return FALSE; }
m_dwCurrentLine = dwIndex;
return TRUE; }
// GetCurrentSectionIteratorLine
//
// Get current line that the iterator has
//
DWORD CIniFile::GetCurrentSectionIteratorLine() { return m_dwCurrentLine; }
// AddLinesToSection
//
// Add Lines to a specific section
// This is what we use to update the ini files with the new options
//
// Note: This will not create the section for you, so do that before
// you call this function
// Note2: If this function fails, it is possible to have part of your
// data in the file, so in essence, it is probably corrupted
//
// Parameters:
// szSectionName - The name of the section to update
// dwNumLines - The number of Lines in **szLines
// szLines - An array of lines to add (Null terminated)
// (no \r\n's are needed)
//
// Return Values:
// TRUE - Added Successfully
// FALSE - Failed to Add
//
BOOL CIniFile::AddLinesToSection(LPTSTR szSectionName, DWORD dwNumLines, LPTSTR *szLines) { CIniFileLine *pCurrentLine; DWORD dwCurrentLineinFile; DWORD dwCurrentLineinInput;
if ( !FindSection( szSectionName ) ) { // Failed to find section, bail now
return FALSE; }
while ( FindNextLineInSection( &pCurrentLine ) ) { // Do nothing, just iterate through the section
}
dwCurrentLineinFile = GetCurrentSectionIteratorLine();
for ( dwCurrentLineinInput = 0; dwCurrentLineinInput < dwNumLines; dwCurrentLineinInput++, dwCurrentLineinFile++ ) { pCurrentLine = AddLine( dwCurrentLineinFile );
if ( !pCurrentLine || !pCurrentLine->CopyLine( szLines[ dwCurrentLineinInput ] ) ) { // We failed to add the line, or to add contents to
// it, so we must bail
return FALSE; } }
return TRUE; }
// LoadFile
//
// Load the ini file from the OS
//
BOOL CIniFile::LoadFile( LPTSTR szFileName ) { HANDLE hFile; BOOL bRet;
hFile = CreateFile( szFileName, GENERIC_READ, // Read
0, // No sharing
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE ) { // Could not open file, Doh!
return FALSE; }
bRet = ReadFileContents( hFile );
// Close File
CloseHandle( hFile );
return bRet; }
// ReadFileContents
//
// Read all of the FileContents into memory
//
BOOL CIniFile::ReadFileContents( HANDLE hFile ) { BYTE pBuffer[INIFILE_READ_CHUNK_SIZE + 2]; // For \0\0
DWORD dwCurrentLocation = 0; DWORD dwBytesRead; BOOL bRet = TRUE; m_bUnicodeFile = FALSE;
if ( ReadFile( hFile, pBuffer, 2, &dwCurrentLocation, NULL ) && ( dwCurrentLocation == 2 ) && ( pBuffer[0] == 0xFF ) && ( pBuffer[1] == 0xFE ) ) { // Since there is the 0xFF, 0xFE we will assume it is unicode
m_bUnicodeFile = TRUE; // Reset Pointer, so we ignore the FFFE.
dwCurrentLocation = 0; }
while ( ReadFile( hFile, pBuffer + dwCurrentLocation, INIFILE_READ_CHUNK_SIZE - dwCurrentLocation, &dwBytesRead, NULL ) && ( dwBytesRead != 0 ) ) { dwCurrentLocation += dwBytesRead; pBuffer[dwCurrentLocation] = '\0'; pBuffer[dwCurrentLocation + 1 ] = '\0';
if ( !LoadChunk( pBuffer, &dwCurrentLocation, FALSE ) ) { bRet = FALSE; break; } }
if ( bRet ) { if ( !LoadChunk( pBuffer, &dwCurrentLocation, TRUE) ) { bRet = FALSE; } }
return bRet; }
// LoadChunk
//
// Load the Chunk of Data from this buffer, into the ini
// class lines
//
// Parameters
// pData - [in/out] Buffer of Bytes to be loaded
// pdwCurrentLocation - [in/out] Location of end of buffer
// bIsLastChunck - [in] Flag to tells us if this is the last chunk
BOOL CIniFile::LoadChunk( LPBYTE pData, DWORD *pdwCurrentLocation, BOOL bIsLastChunk ) { LPBYTE szEndofLine; LPBYTE szNextLine; CIniFileLine *pCurrentLine;
while ( *pdwCurrentLocation > 0 ) { szEndofLine = m_bUnicodeFile ? (LPBYTE) wcsstr( (LPWSTR) pData, L"\r" ) : (LPBYTE) strstr( (LPSTR) pData, "\r" );
if ( szEndofLine == NULL ) { szEndofLine = m_bUnicodeFile ? (LPBYTE) wcsstr( (LPWSTR) pData, L"\n" ) : (LPBYTE) strstr( (LPSTR) pData, "\n" ); // Move to next line (this is \n, so it is only 1 char away)
szNextLine = szEndofLine + ( m_bUnicodeFile ? 2 : 1 ); } else { // \r was found
if ( m_bUnicodeFile ) { // Jump past \r, or \r\n
szNextLine = szEndofLine + ( *( (LPWSTR) szEndofLine + 1 ) == '\n' ? 4 : 2 ); } else { // Jump past \r, or \r\n
szNextLine = szEndofLine + ( *( (LPSTR) szEndofLine + 1) == '\n' ? 2 : 1 ); }
if ( !bIsLastChunk && ( m_bUnicodeFile ? *( (LPWSTR) szEndofLine + 1) == '\0' : *( (LPSTR) szEndofLine + 1) == '\0' ) ) { // This is a \r with a \0 imediately after it, this might mean that we have
// not read enough, so lets return and lets read another chunk
return TRUE; } }
if ( !szEndofLine ) { if ( bIsLastChunk ) { szEndofLine = pData + *pdwCurrentLocation; szNextLine = szEndofLine; } else { // Can not find another line, so exit
return TRUE; } }
// Null Terminate Here
if ( m_bUnicodeFile ) { wcscpy( (LPWSTR) szEndofLine, L"\0" ); } else { strcpy( (LPSTR) szEndofLine, "\0" ); }
if ( ( ( pCurrentLine = AddLine( GetNumberofLines() ) ) == NULL ) || !( m_bUnicodeFile ? pCurrentLine->CopyLine( (LPWSTR) pData ) : pCurrentLine->CopyLine( (LPSTR) pData ) ) ) { // Error Adding Line
return FALSE; }
// Remove that part of the line
memmove( pData, szNextLine, *pdwCurrentLocation - (szNextLine - pData) + 2 ); // +2 for \0\0
*pdwCurrentLocation -= (DWORD) (szNextLine - pData); }
return TRUE; }
// SaveFile
//
// Save the ini file to disk
//
BOOL CIniFile::SaveFile( LPTSTR szFileName ) { HANDLE hFile; BOOL bRet;
hFile = CreateFile( szFileName, GENERIC_WRITE, // Read
0, // No sharing
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE ) { // Could not create the file
return FALSE; }
bRet = WriteFileContents( hFile );
// Close File
CloseHandle( hFile );
return bRet; }
// WriteFileContents
//
// Write the ini contents to the file
//
BOOL CIniFile::WriteFileContents( HANDLE hFile ) { DWORD dwCurrent; CHAR szLine[MAX_PATH]; DWORD dwBytesWritten;
if ( m_bUnicodeFile ) { // If this file is unicode, lets write the prefix 0xFF,0xFE right now
BYTE pData[2] = { 0xFF, 0xFE };
if ( !WriteFile( hFile, pData, sizeof( pData ), &dwBytesWritten, NULL ) ) { // Failed to write to file
return FALSE; } }
for ( dwCurrent = 0; dwCurrent < GetNumberofLines(); dwCurrent++ ) { if ( !m_bUnicodeFile ) { // If not unicode, then we need to translate back to Ansi
if ( !WideCharToMultiByte( CP_ACP, //CP_THREAD_ACP doesn't work on NT4
0, GetLine( dwCurrent )->QueryLine(), -1, // Null terminated
szLine, MAX_PATH, NULL, NULL) ) { // Line could not be converted, so lets fail
return FALSE; } }
// Write line to file
if ( !WriteFile( hFile, m_bUnicodeFile ? (LPBYTE) GetLine( dwCurrent )->QueryLine(): (LPBYTE) szLine, m_bUnicodeFile ? (DWORD) wcslen( GetLine( dwCurrent )->QueryLine() ) * sizeof( WCHAR ): (DWORD) strlen( szLine ) * sizeof( CHAR), &dwBytesWritten, NULL ) ) { // Failed to write Line
return FALSE; } // Line Terminators
if ( !WriteFile( hFile, m_bUnicodeFile ? (LPBYTE) L"\r\n": (LPBYTE) "\r\n", m_bUnicodeFile ? (DWORD) wcslen( L"\r\n" ) * sizeof( WCHAR ) : (DWORD) strlen( "\r\n") * sizeof( CHAR) , &dwBytesWritten, NULL ) ) { // Failed to write Line
return FALSE; }
} // for
return TRUE; }
|