Re: Q: how best to use INI style files

From: Niklas Borson (niklasb_at_microsoft.com)
Date: 06/09/04


Date: 9 Jun 2004 14:55:10 -0700


"Jim Backus" <jhb@nospam.co.uk> wrote in message news:<TpquPuPd0tCd-pn2-s29lzxP4hyPV@localhost>...
[snip]
> My question is: what is the best or recommended way of doing this in
> C?
>
> I've thought of two ways:
>
> 1. Read in a line and parse it so that the part before the equals sign
> is a variable name and the part after the equals sign is the
> variable's value.
>
> 2. Declare all possible variables in the C program and parse the "ini"
> file setting any variables found to the value after the equals sign.

Well, one answer of course is to use an existing library or API,
such as the various "Profile" functions in Windows. However, let's
assume you want to read an .ini file yourself using only standard C.

I would use a combination of the two approaches you propose.
Presumably, the end result of reading the .ini file will be
some kind of application-specific data structures representing
your program configuration. Most likely these data structures
will not consist solely of name/value string pairs. Thus, your
approach #2 seems on-target.

However, you don't want to have the nitty gritty details of .ini
file parsing scattered throughout your code. It makes more sense
to have a set of low-level input routines that know how to read
an .ini file, tokenize it, remove comments and white space,
process quotation marks, etc. This input code should be clearly
separated (via a well-designed interface) from the code that
maintains your application's state. So your approach #1 is also
right, in the sense that you want to have some functions that
read and tokenize lines of an .ini file.

One nice thing is that the input functions don't really need to
"know" anything about the rest of your programs. Therefore, if
design them well you should be able to reuse them over and over.
Following is one possible programming interface for the .ini
file input functions. The implementation is left as an
exercise. :-)

// IniReader - opaque structure representing an open .ini file.
// This structure would be defined in the same source file
// as the various Ini* functions. It would be allocated by
// IniOpen and freed by IniClose.
typedef struct tagIniReader IniReader;

// IniReadResult - represents the result of an IniReadLine.
enum IniReadResult
{
    ini_eof,
    ini_section,
    ini_variable
};

// IniOpen - opens an .ini file, and returns an IniReader pointer
// if successful or NULL if not. The pointer must later be
// freed using IniClose.
IniReader* IniOpen(const char* fileName);

// IniClose - closes the .ini file and frees resources.
void IniClose(IniReader* file);

// IniReadLine - reads a single line and updates the IniReader
// accordingly; the contents of the current line can be
// accessed using the IniGet* functions.
IniReadResult IniReadLine(IniReader* file);

// IniGetSectionName - returns the name of the current
// section, or NULL if there is no current section.
const char* IniGetSectionName(IniReader* file);

// IniGetVarName - returns the name of the current variable,
// or NULL if the current line is not a variable.
const char* IniGetVarName(IniReader* file);

// IniGetValue - returns the value of the current variable,
// or NULL if the current line is not a variable.
const char* IniGetValue(IniReader* file);

Your application's initialization code would then use these
functions to read the .ini file line-by-line and initialize the
application's state accordingly.

Alternatively, you may find that your initialization code would
be simplified if you could look up sections and variables at
random instead of reading them sequentially. If so, you could
design another set of reusable functions to provide the random
lookup capability.

Again, I'll suggest a possible interface (which you can use
or not as you see fit) and the implimentation is up to you.
As to how it would be used, your app would open the file
using IniFileOpen(), use the various Find* methods to look
up the information it needed, and then close the file using
IniFileClose() -- the end result being that you would have
initialized some sort of application-specific data structures.

// opaque structures
typedef struct tagIniFile IniFile;
typedef struct tagIniSection IniSection;

// open/close methods
IniFile* IniFileOpen(const char* fileName);
void IniFileClose(IniFile* file);

// lookup methods
IniSection* FindIniFileSection(
    IniFile* file,
    const char* sectionName);

const char* FindIniSectionVar(
    IniSection* section,
    const char* varName);

const char* FindIniFileVar(
    IniFile* file,
    const char* sectionName,
    const char* varName);

Note that this second second set of functions would be
implemented in terms of the first set of functions. Thus,
there's only one set of code responsible with low-level
input. If you find a bug with (say) how comments are
processed, there's only one place you'll need to fix it.

--Nick



Relevant Pages

  • Re: problem writing in INI file with WritePrivateProfileString
    ... const char* pszName, const char* pszData) ... WritePrivateProfileString (pszSection, ... > it writes all selected parameters in the INI file. ... > internally calls WritePrivateProfileString: ...
    (microsoft.public.vc.mfc)
  • WPP extended format specification strings
    ... In fact, several other types mentioned on the MSDN page is not supported, e.g. "%!time!", and according to the INI file several more types are supported that are not listed on MSDN. ... DEFINE_CPLX_TYPE(WSTR, WPP_LOGASTR, const char *, ItemString, "s", S, 0); ...
    (microsoft.public.development.device.drivers)