708 lines
19 KiB
C
708 lines
19 KiB
C
/************************************************************************
|
|
Filename : IniFile.C
|
|
Version : 0.51
|
|
Author(s) : Carsten Breuer
|
|
--[ Description ]-------------------------------------------------------
|
|
|
|
This file contains a complete interface to read and write ini files
|
|
like windows do it. It's also avaiable as a C++ class.
|
|
|
|
--[ History ] ----------------------------------------------------------
|
|
|
|
0.10: Original file by Carsten Breuer. First beta version.
|
|
0.20: Some bugs resolved and some suggestions from
|
|
jim hall (freedos.org) implemented.
|
|
0.30: Some stuff for unix added. They dont know strupr.
|
|
Thanks to Dieter Engelbrecht (dieter@wintop.net).
|
|
0.40: Bug at WriteString fixed.
|
|
0.50: Problem with file pointer solved
|
|
0.51: We better do smaller steps now. I have reformated to tab4. Sorry
|
|
New function DeletepKey added. Thanks for the guy who post this.
|
|
|
|
--[ How to compile ]----------------------------------------------------
|
|
|
|
This file was developed under DJGPP and Rhide. If you are familiar with
|
|
Borland C++ 3.1, you will feel like at home ;-)).
|
|
Both tools are free software.
|
|
|
|
Downloads at: http://www.delorie.com/djgpp
|
|
|
|
|
|
--[ Where to get help/information ]-------------------------------------
|
|
|
|
The author : C.Breuer@OpenWin.de
|
|
|
|
--[ License ] ----------------------------------------------------------
|
|
|
|
LGPL (Free for private and comercial use)
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
------------------------------------------------------------------------
|
|
Copyright (c) 2000 Carsten Breuer
|
|
************************************************************************/
|
|
|
|
/* defines for, or consts and inline functions for C++ */
|
|
|
|
/* global includes */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* local includes */
|
|
#include "inifile.h"
|
|
|
|
/* Global Variables */
|
|
|
|
struct ENTRY *Entry = NULL;
|
|
struct ENTRY *CurEntry = NULL;
|
|
char Result[520] =
|
|
{""};
|
|
FILE *IniFile;
|
|
|
|
/* Private functions declarations */
|
|
void AddpKey(struct ENTRY *Entry, cchr *pKey, cchr *Value);
|
|
void FreeMem(void *Ptr);
|
|
void FreeAllMem(void);
|
|
bool FindpKey(cchr *Section, cchr *pKey, EFIND *List);
|
|
bool AddSectionAndpKey(cchr *Section, cchr *pKey, cchr *Value);
|
|
struct ENTRY *MakeNewEntry(void);
|
|
|
|
/*=========================================================================
|
|
strupr -de-
|
|
-------------------------------------------------------------------------
|
|
Job : String to Uppercase 22.03.2001 Dieter Engelbrecht dieter@wintop.net
|
|
*========================================================================*/
|
|
#ifdef DONT_HAVE_STRUPR
|
|
/* DONT_HAVE_STRUPR is set when INI_REMOVE_CR is defined */
|
|
void strupr(char *str)
|
|
{
|
|
// We dont check the ptr because the original also dont do it.
|
|
while (*str != 0)
|
|
{
|
|
if (islower(*str))
|
|
{
|
|
*str = toupper(*str);
|
|
}
|
|
str++;
|
|
}
|
|
}
|
|
#else
|
|
#endif
|
|
|
|
/*=========================================================================
|
|
OpenIniFile
|
|
-------------------------------------------------------------------------
|
|
Job : Opens an ini file or creates a new one if the requested file
|
|
doesnt exists.
|
|
|
|
Att : Be sure to call CloseIniFile to free all mem allocated during
|
|
operation!
|
|
*========================================================================*/
|
|
bool OpenIniFile(cchr *FileName)
|
|
{
|
|
char Str[512];
|
|
char *pStr;
|
|
struct ENTRY *pEntry;
|
|
|
|
FreeAllMem();
|
|
|
|
if (FileName == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ((IniFile = fopen(FileName, "r")) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
while (fgets(Str, 512, IniFile) != NULL)
|
|
{
|
|
pStr = strchr(Str, '\n');
|
|
if (pStr != NULL)
|
|
{
|
|
*pStr = 0;
|
|
}
|
|
pEntry = MakeNewEntry();
|
|
if (pEntry == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef INI_REMOVE_CR
|
|
Len = strlen(Str);
|
|
if (Len > 0)
|
|
{
|
|
if (Str[Len - 1] == '\r')
|
|
{
|
|
Str[Len - 1] = '\0';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pEntry->Text = (char *)malloc(strlen(Str) + 1);
|
|
if (pEntry->Text == NULL)
|
|
{
|
|
FreeAllMem();
|
|
return FALSE;
|
|
}
|
|
strcpy(pEntry->Text, Str);
|
|
pStr = strchr(Str, ';');
|
|
if (pStr != NULL)
|
|
{
|
|
*pStr = 0;
|
|
} /* Cut all comments */
|
|
if ((strstr(Str, "[") > 0) && (strstr(Str, "]") > 0)) /* Is Section */
|
|
{
|
|
pEntry->Type = tpSECTION;
|
|
}
|
|
else
|
|
{
|
|
if (strstr(Str, "=") > 0)
|
|
{
|
|
pEntry->Type = tpKEYVALUE;
|
|
}
|
|
else
|
|
{
|
|
pEntry->Type = tpCOMMENT;
|
|
}
|
|
}
|
|
CurEntry = pEntry;
|
|
}
|
|
fclose(IniFile);
|
|
IniFile = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
/*=========================================================================
|
|
CloseIniFile
|
|
-------------------------------------------------------------------------
|
|
Job : Frees the memory and closes the ini file without any
|
|
modifications. If you want to write the file use
|
|
WriteIniFile instead.
|
|
*========================================================================*/
|
|
void CloseIniFile(void)
|
|
{
|
|
FreeAllMem();
|
|
if (IniFile != NULL)
|
|
{
|
|
fclose(IniFile);
|
|
IniFile = NULL;
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
WriteIniFile
|
|
-------------------------------------------------------------------------
|
|
Job : Writes the iniFile to the disk and close it. Frees all memory
|
|
allocated by WriteIniFile;
|
|
==========================================================================*/
|
|
bool WriteIniFile(const char *FileName)
|
|
{
|
|
struct ENTRY *pEntry = Entry;
|
|
IniFile = NULL;
|
|
if (IniFile != NULL)
|
|
{
|
|
fclose(IniFile);
|
|
}
|
|
if ((IniFile = fopen(FileName, "wb")) == NULL)
|
|
{
|
|
FreeAllMem();
|
|
return FALSE;
|
|
}
|
|
|
|
while (pEntry != NULL)
|
|
{
|
|
if (pEntry->Type != tpNULL)
|
|
{
|
|
fprintf(IniFile, "%s\n", pEntry->Text);
|
|
pEntry = pEntry->pNext;
|
|
}
|
|
}
|
|
|
|
fclose(IniFile);
|
|
IniFile = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
/*=========================================================================
|
|
WriteString : Writes a string to the ini file
|
|
*========================================================================*/
|
|
void WriteString(cchr *Section, cchr *pKey, cchr *Value)
|
|
{
|
|
EFIND List;
|
|
char Str[512];
|
|
|
|
if (ArePtrValid(Section, pKey, Value) == FALSE)
|
|
{
|
|
return;
|
|
}
|
|
if (FindpKey(Section, pKey, &List) == TRUE)
|
|
{
|
|
sprintf(Str, "%s=%s%s", List.KeyText, Value, List.Comment);
|
|
FreeMem(List.pKey->Text);
|
|
List.pKey->Text = (char *)malloc(strlen(Str) + 1);
|
|
strcpy(List.pKey->Text, Str);
|
|
}
|
|
else
|
|
{
|
|
if ((List.pSec != NULL) && (List.pKey == NULL)) /* section exist, pKey not */
|
|
{
|
|
AddpKey(List.pSec, pKey, Value);
|
|
}
|
|
else
|
|
{
|
|
AddSectionAndpKey(Section, pKey, Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
WriteBool : Writes a boolean to the ini file
|
|
*========================================================================*/
|
|
void WriteBool(cchr *Section, cchr *pKey, bool Value)
|
|
{
|
|
char Val[2] = {'0', 0};
|
|
if (Value != 0)
|
|
{
|
|
Val[0] = '1';
|
|
}
|
|
WriteString(Section, pKey, Val);
|
|
}
|
|
|
|
/*=========================================================================
|
|
WriteInt : Writes an integer to the ini file
|
|
*========================================================================*/
|
|
void WriteInt(cchr *Section, cchr *pKey, int Value)
|
|
{
|
|
char Val[12]; /* 32bit maximum + sign + \0 */
|
|
sprintf(Val, "%d", Value);
|
|
WriteString(Section, pKey, Val);
|
|
}
|
|
|
|
/*=========================================================================
|
|
WriteLong : Writes an long to the ini file
|
|
*========================================================================*/
|
|
void WriteLong(cchr *Section, cchr *pKey, long Value)
|
|
{
|
|
char Val[22]; /* 64bit maximum + sign + \0 */
|
|
sprintf(Val, "%ld", Value);
|
|
WriteString(Section, pKey, Val);
|
|
}
|
|
|
|
/*=========================================================================
|
|
WriteDouble : Writes a double to the ini file
|
|
*========================================================================*/
|
|
void WriteDouble(cchr *Section, cchr *pKey, double Value)
|
|
{
|
|
char Val[32]; /* DDDDDDDDDDDDDDD+E308\0 */
|
|
sprintf(Val, "%1.10lE", Value);
|
|
WriteString(Section, pKey, Val);
|
|
}
|
|
|
|
/*=========================================================================
|
|
ReadString : Reads a string from the ini file
|
|
*========================================================================*/
|
|
const char *
|
|
ReadString(cchr *Section, cchr *pKey, cchr *Default)
|
|
{
|
|
EFIND List;
|
|
if (ArePtrValid(Section, pKey, Default) == FALSE)
|
|
{
|
|
return Default;
|
|
}
|
|
if (FindpKey(Section, pKey, &List) == TRUE)
|
|
{
|
|
strcpy(Result, List.ValText);
|
|
return Result;
|
|
}
|
|
return Default;
|
|
}
|
|
|
|
/*=========================================================================
|
|
ReadBool : Reads a boolean from the ini file
|
|
*========================================================================*/
|
|
bool ReadBool(cchr *Section, cchr *pKey, bool Default)
|
|
{
|
|
char Val[2] = {"0"};
|
|
if (Default != 0)
|
|
{
|
|
Val[0] = '1';
|
|
}
|
|
return (atoi(ReadString(Section, pKey, Val)) ? 1 : 0); /* Only 0 or 1 allowed */
|
|
}
|
|
|
|
/*=========================================================================
|
|
ReadInt : Reads a integer from the ini file
|
|
*========================================================================*/
|
|
int ReadInt(cchr *Section, cchr *pKey, int Default)
|
|
{
|
|
char Val[12];
|
|
sprintf(Val, "%d", Default);
|
|
return (atoi(ReadString(Section, pKey, Val)));
|
|
}
|
|
|
|
/*=========================================================================
|
|
ReadLong : Reads a long from the ini file
|
|
*========================================================================*/
|
|
long ReadLong(cchr *Section, cchr *pKey, long Default)
|
|
{
|
|
char Val[22];
|
|
sprintf(Val, "%ld", Default);
|
|
return (atol(ReadString(Section, pKey, Val)));
|
|
}
|
|
|
|
/*=========================================================================
|
|
ReadDouble : Reads a double from the ini file
|
|
*========================================================================*/
|
|
double
|
|
ReadDouble(cchr *Section, cchr *pKey, double Default)
|
|
{
|
|
double Val;
|
|
sprintf(Result, "%1.10lE", Default);
|
|
sscanf(ReadString(Section, pKey, Result), "%lE", &Val);
|
|
return Val;
|
|
}
|
|
|
|
/*=========================================================================
|
|
DeleteKey : Deletes a pKey from the ini file.
|
|
*========================================================================*/
|
|
|
|
bool DeleteKey(cchr *Section, cchr *pKey)
|
|
{
|
|
EFIND List;
|
|
struct ENTRY *pPrev;
|
|
struct ENTRY *pNext;
|
|
|
|
if (FindpKey(Section, pKey, &List) == TRUE)
|
|
{
|
|
pPrev = List.pKey->pPrev;
|
|
pNext = List.pKey->pNext;
|
|
if (pPrev)
|
|
{
|
|
pPrev->pNext = pNext;
|
|
}
|
|
if (pNext)
|
|
{
|
|
pNext->pPrev = pPrev;
|
|
}
|
|
FreeMem(List.pKey->Text);
|
|
FreeMem(List.pKey);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Here we start with our helper functions */
|
|
/*=========================================================================
|
|
FreeMem : Frees a pointer. It is set to NULL by Free AllMem
|
|
*========================================================================*/
|
|
void FreeMem(void *Ptr)
|
|
{
|
|
if (Ptr != NULL)
|
|
{
|
|
free(Ptr);
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
FreeAllMem : Frees all allocated memory and set the pointer to NULL.
|
|
Thats IMO one of the most important issues relating
|
|
to pointers :
|
|
|
|
A pointer is valid or NULL.
|
|
*========================================================================*/
|
|
void FreeAllMem(void)
|
|
{
|
|
struct ENTRY *pEntry;
|
|
struct ENTRY *pNextEntry;
|
|
pEntry = Entry;
|
|
while (1)
|
|
{
|
|
if (pEntry == NULL)
|
|
{
|
|
break;
|
|
}
|
|
pNextEntry = pEntry->pNext;
|
|
FreeMem(pEntry->Text); /* Frees the pointer if not NULL */
|
|
FreeMem(pEntry);
|
|
pEntry = pNextEntry;
|
|
}
|
|
Entry = NULL;
|
|
CurEntry = NULL;
|
|
}
|
|
|
|
/*=========================================================================
|
|
FindSection : Searches the chained list for a section. The section
|
|
must be given without the brackets!
|
|
Return Value: NULL at an error or a pointer to the ENTRY structure
|
|
if succeed.
|
|
*========================================================================*/
|
|
struct ENTRY *
|
|
FindSection(cchr *Section)
|
|
{
|
|
char Sec[130];
|
|
char iSec[130];
|
|
struct ENTRY *pEntry;
|
|
sprintf(Sec, "[%s]", Section);
|
|
strupr(Sec);
|
|
pEntry = Entry; /* Get a pointer to the first Entry */
|
|
while (pEntry != NULL)
|
|
{
|
|
if (pEntry->Type == tpSECTION)
|
|
{
|
|
strcpy(iSec, pEntry->Text);
|
|
strupr(iSec);
|
|
if (strcmp(Sec, iSec) == 0)
|
|
{
|
|
return pEntry;
|
|
}
|
|
}
|
|
pEntry = pEntry->pNext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*=========================================================================
|
|
FindpKey : Searches the chained list for a pKey under a given section
|
|
Return Value: NULL at an error or a pointer to the ENTRY structure
|
|
if succeed.
|
|
*========================================================================*/
|
|
bool FindpKey(cchr *Section, cchr *pKey, EFIND *List)
|
|
{
|
|
char Search[130];
|
|
char Found[130];
|
|
char Text[512];
|
|
char *pText;
|
|
struct ENTRY *pEntry;
|
|
List->pSec = NULL;
|
|
List->pKey = NULL;
|
|
pEntry = FindSection(Section);
|
|
if (pEntry == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
List->pSec = pEntry;
|
|
List->KeyText[0] = 0;
|
|
List->ValText[0] = 0;
|
|
List->Comment[0] = 0;
|
|
pEntry = pEntry->pNext;
|
|
if (pEntry == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
sprintf(Search, "%s", pKey);
|
|
strupr(Search);
|
|
while (pEntry != NULL)
|
|
{
|
|
if ((pEntry->Type == tpSECTION) || /* Stop after next section or EOF */
|
|
(pEntry->Type == tpNULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (pEntry->Type == tpKEYVALUE)
|
|
{
|
|
strcpy(Text, pEntry->Text);
|
|
pText = strchr(Text, ';');
|
|
if (pText != NULL)
|
|
{
|
|
strcpy(List->Comment, Text);
|
|
*pText = 0;
|
|
}
|
|
pText = strchr(Text, '=');
|
|
if (pText != NULL)
|
|
{
|
|
*pText = 0;
|
|
strcpy(List->KeyText, Text);
|
|
strcpy(Found, Text);
|
|
*pText = '=';
|
|
strupr(Found);
|
|
/* printf ("%s,%s\n", Search, Found); */
|
|
if (strcmp(Found, Search) == 0)
|
|
{
|
|
strcpy(List->ValText, pText + 1);
|
|
List->pKey = pEntry;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
pEntry = pEntry->pNext;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*=========================================================================
|
|
AddItem : Adds an item (pKey or section) to the chaines list
|
|
*========================================================================*/
|
|
bool AddItem(char Type, cchr *Text)
|
|
{
|
|
struct ENTRY *pEntry = MakeNewEntry();
|
|
if (pEntry == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pEntry->Type = Type;
|
|
pEntry->Text = (char *)malloc(strlen(Text) + 1);
|
|
if (pEntry->Text == NULL)
|
|
{
|
|
free(pEntry);
|
|
return FALSE;
|
|
}
|
|
strcpy(pEntry->Text, Text);
|
|
pEntry->pNext = NULL;
|
|
if (CurEntry != NULL)
|
|
{
|
|
CurEntry->pNext = pEntry;
|
|
}
|
|
CurEntry = pEntry;
|
|
return TRUE;
|
|
}
|
|
|
|
/*=========================================================================
|
|
AddItemAt : Adds an item at a selected position. This means, that the
|
|
chained list will be broken at the selected position and
|
|
that the new item will be Inserted.
|
|
Before : A.Next = &B
|
|
After : A.Next = &NewItem, NewItem.Next = &B
|
|
*========================================================================*/
|
|
bool AddItemAt(struct ENTRY *EntryAt, char Mode, cchr *Text)
|
|
{
|
|
struct ENTRY *pNewEntry;
|
|
if (EntryAt == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pNewEntry = (struct ENTRY *)malloc(sizeof(ENTRY));
|
|
if (pNewEntry == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pNewEntry->Text = (char *)malloc(strlen(Text) + 1);
|
|
if (pNewEntry->Text == NULL)
|
|
{
|
|
free(pNewEntry);
|
|
return FALSE;
|
|
}
|
|
strcpy(pNewEntry->Text, Text);
|
|
if (EntryAt->pNext == NULL) /* No following nodes. */
|
|
{
|
|
EntryAt->pNext = pNewEntry;
|
|
pNewEntry->pNext = NULL;
|
|
}
|
|
else
|
|
{
|
|
pNewEntry->pNext = EntryAt->pNext;
|
|
EntryAt->pNext = pNewEntry;
|
|
}
|
|
pNewEntry->pPrev = EntryAt;
|
|
pNewEntry->Type = Mode;
|
|
return TRUE;
|
|
}
|
|
|
|
/*=========================================================================
|
|
AddSectionAndpKey : Adds a section and then a pKey to the chained list
|
|
*========================================================================*/
|
|
bool AddSectionAndpKey(cchr *Section, cchr *pKey, cchr *Value)
|
|
{
|
|
char Text[512];
|
|
sprintf(Text, "[%s]", Section);
|
|
if (AddItem(tpSECTION, Text) == FALSE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
sprintf(Text, "%s=%s", pKey, Value);
|
|
return AddItem(tpKEYVALUE, Text);
|
|
}
|
|
|
|
/*=========================================================================
|
|
AddpKey : Adds a pKey to the chained list
|
|
*========================================================================*/
|
|
void AddpKey(struct ENTRY *SecEntry, cchr *pKey, cchr *Value)
|
|
{
|
|
char Text[512];
|
|
sprintf(Text, "%s=%s", pKey, Value);
|
|
AddItemAt(SecEntry, tpKEYVALUE, Text);
|
|
}
|
|
|
|
/*=========================================================================
|
|
MakeNewEntry : Allocates the memory for a new entry. This is only
|
|
the new empty structure, that must be filled from
|
|
function like AddItem etc.
|
|
Info : This is only a internal function. You dont have to call
|
|
it from outside.
|
|
*==========================================================================*/
|
|
struct ENTRY *
|
|
MakeNewEntry(void)
|
|
{
|
|
struct ENTRY *pEntry;
|
|
pEntry = (struct ENTRY *)malloc(sizeof(ENTRY));
|
|
if (pEntry == NULL)
|
|
{
|
|
FreeAllMem();
|
|
return NULL;
|
|
}
|
|
if (Entry == NULL)
|
|
{
|
|
Entry = pEntry;
|
|
}
|
|
pEntry->Type = tpNULL;
|
|
pEntry->pPrev = CurEntry;
|
|
pEntry->pNext = NULL;
|
|
pEntry->Text = NULL;
|
|
if (CurEntry != NULL)
|
|
{
|
|
CurEntry->pNext = pEntry;
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
/*=========================================================================
|
|
GetSectionCount : Get the number of sections
|
|
*========================================================================*/
|
|
int GetSectionCount()
|
|
{
|
|
struct ENTRY *pEntry = Entry;
|
|
int count = 0;
|
|
while (pEntry != NULL)
|
|
{
|
|
if (pEntry->Type == tpSECTION)
|
|
{
|
|
count++;
|
|
}
|
|
pEntry = pEntry->pNext;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*=========================================================================
|
|
GetSections : Retrieve the sections
|
|
*========================================================================*/
|
|
void GetSections(char *sections[])
|
|
{
|
|
struct ENTRY *pEntry = Entry;
|
|
int i = 0;
|
|
|
|
while (pEntry != NULL)
|
|
{
|
|
if (pEntry->Type == tpSECTION)
|
|
{
|
|
sprintf(sections[i], "%s", pEntry->Text);
|
|
i++;
|
|
}
|
|
pEntry = pEntry->pNext;
|
|
}
|
|
}
|