INI-Style Sections and Entries

The INI file is the old Windows standard for keeping program settings, preferences and configurations. Here is an example of an INI file. Each section starts with a bracketed section name and has a list of key=value entries.

[Settings]
Count=10
User=John Smith
[Files]
Database=test2.txt

Here is an example XML format to contain the same information. You might want to use the section names and entry names (keys) as tag names in your XML format, but if there is any chance your section and entry names will contain spaces or symbols then its better to put them in attributes since spaces and certain symbols are not allowed in XML tag names.

<Settings>
    <Section name="Settings">
        <Entry name="Count">10</Entry>
        <Entry name="User">John Smith</Entry>
    </Section>
    <Section name="Files">
        <Entry name="Database">test2.txt</Entry>
    </Section>
</Settings>

The nice thing about using XML is that your types of data in the future won't be limited to simple entries. You can later introduce entries containing lists of elements and entries containing persisted objects, or even additional entry attributes such as timestamps and modified flags.

You can use the following SetEntry and FindEntry methods much like you use the Win32 functions WritePrivateProfileString and GetPrivateProfileString. They should be implemented in your own program, not as members of the CMarkup class itself, because they are likely to be modified to conform to your needs. Here is sample code that uses the functions on the above INI-style example.

CMarkup xml;
SetEntry( xml, "Settings", "Count", "10" );
SetEntry( xml, "Settings", "User", "John Smith" );
SetEntry( xml, "Files", "Database", "test1.txt" );
SetEntry( xml, "Files", "Database", "test2.txt" ); // overwrite
CString csUser;
ASSERT( FindEntry(xml,"Settings","User",csUser) );
ASSERT( csUser == "John Smith" );

Here are sample SetEntry and FindEntry function implementations.

void SetEntry( CMarkup& xml,
  CString csSection, CString csEntry, CString csValue )
{
  // Find/Create root element of xml document
  xml.ResetPos();

  // Put your root element name here
  if ( ! xml.FindElem() )
    xml.AddElem( "Settings" );

  // Find/Create section
  BOOL bFoundSection = FALSE;
  while ( xml.FindChildElem("Section") )
  {
    // Is this the right section?
    if ( xml.GetChildAttrib("name") == csSection )
    {
      bFoundSection = TRUE;
      break;
    }
  }
  if ( ! bFoundSection )
  {
    xml.AddChildElem( "Section" );
    xml.SetChildAttrib( "name", csSection );
  }

  // Find/Create entry
  xml.IntoElem();
  BOOL bFoundEntry = FALSE;
  while ( xml.FindChildElem("Entry") )
  {
    // Is this the right entry?
    if ( xml.GetChildAttrib("name") == csEntry )
    {
      bFoundEntry = TRUE;
      break;
    }
  }
  if ( ! bFoundEntry )
  {
    xml.AddChildElem( "Entry" );
    xml.SetChildAttrib( "name", csEntry );
  }

  // Set value
  xml.SetChildData( csValue );
}

bool FindEntry( CMarkup& xml,
  CString csSection, CString csEntry, CString& csValue )
{
  // Loop through sections
  xml.ResetPos();
  while ( xml.FindChildElem("Section") )
  {
    // Is this the right section?
    if ( xml.GetChildAttrib("name") == csSection )
    {
      // Check entries in this section
      xml.IntoElem();
      while ( xml.FindChildElem("Entry") )
      {
        // Is this the right entry?
        if ( xml.GetChildAttrib("name") == csEntry )
        {
          csValue = xml.GetChildData();
          return TRUE;
        }
      }
      break; // don't check any other sections
    }
  }
  return FALSE;
}