Simple ini file parser
Posted: Thu Jan 16, 2014 8:44 pm
Hi all, I originally wrote this as a part of my final year project.
This code demonstrate how you could use state machines to parse ini file and read it's content into memory structure.
interface.
I'm going to release the code to github so you guys just could download the tarball or use this as a static library.
To use the code follow this example.
along with this file ,
config_file_test.ini
To compile you may invoke these commands like bellow.
This code demonstrate how you could use state machines to parse ini file and read it's content into memory structure.
Code: Select all
/* LICENSE GNU GPL
*
* FILE: config_file.cpp
implementation of the simple ini file parser.
* Feel free to use this in your commercial or non-commercial software.
*
*
*
*
* Written By: [email protected]
*
*/
#include "config_file.h"
#include "common.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static FILE* fd=0; /* file descriptor for the open file */
typedef struct tag_key_value
{
char* key ;
char* value ;
tag_key_value * next;
} key_value;
typedef struct tag_session{
char* session_name ;
key_value *head_key_value;
struct tag_session* next;
} session;
session * head;
session * current_session;
static session* get_next_session(session *current_session)
{
if ( current_session == NULL)
std::cerr << "ConfigFile: current session is NULL.\n";
return current_session->next;
}
int config_file_session_into(const char * session_name)
{
/* check for preconditions */
if(session_name == NULL)
{
std::cerr << "ConfigFile:config_file_session_into could not enter \
\n into NULL session.\n";
exit(0);
}
if( strcmp( session_name ,"")==0){
std::cerr <<"ConfigFile:config_file_session_into could not enter into\
\n\"\"session.\n";
exit(0);
}
if(head ==NULL)
{
std::cerr << "ConfigFile:You need to call config_file_parse() before config_file_session_into(). \n";
exit(0);
}
session* previous_session = current_session;
current_session = head;
while(TRUE)
{
if( strcmp( session_name , current_session->session_name ) != 0)
{
current_session = current_session->next;
if(current_session == NULL){
/* we didn't find the session that we looking for
* This is the termination condition.
*/
current_session =previous_session;
return FALSE;
}
}else{
::current_session = current_session;
return TRUE;
}
}
}
const char* config_file_get_current_session()
{
if(current_session == NULL)
return NULL;
else
return current_session->session_name;
}
const char* config_file_get_key_value(const char* keyword)
{
if( current_session == NULL)
{
std::cerr << "ConfigFile: Please step into a session before calling \
\n config_file_get_key_value().\n";
exit(0);
}
key_value* current_key_value = current_session->head_key_value;
while(TRUE)
{
if( current_key_value == NULL)
{
std::cerr << "ConfigFile: Required Keyword '" << keyword <<"' could not be\nfound\n";
return NULL;
}
if ( strcmp( current_key_value->key,keyword) != 0)
{
current_key_value = current_key_value->next;
continue;
}else{
char * ret_ = strdup(current_key_value->value);
return ret_;
}
}
}
/* .some utility macros. */
#define IS_WHITE_SPACE(X) (strchr(" \t",X)?TRUE:FALSE)
#define IS_NEW_LINE(X) (strchr("\n\r",X)?TRUE:FALSE)
#define IS_UPPER_ALPHA(X) ((X <= 'Z' && X >= 'A')?TRUE:FALSE)
#define IS_LOWER_ALPHA(X) ((X <='z' && X >= 'a' )?TRUE:FALSE)
#define IS_ALPHA(X) ((IS_UPPER_ALPHA(X) || IS_LOWER_ALPHA(X) )?TRUE:FALSE)
#define IS_NUM(X) (strchr("0123456789",X)?TRUE:FALSE)
#define IS_ALPHANUM(X) ((IS_ALPHA(X) || IS_NUM(X) )?TRUE:FALSE)
/* some utility functions */
static char* appendchar(char *str, char ch)
{
if(str==NULL)
std::cerr << "ConfigFile: Could not b appened into a NULL string.\n";
int length = strlen(str);
if(length == BUF_SIZE -1 ){
std::cerr << "ConfigFile:appendchar could not append more.Buffer full.\n";
exit(0);
}
str[length]=ch;
str[length+1]=0; // append the null-termination character. //
}
/* printsessions() */
static void print_sessions()
{
if(head== NULL)
{
printf("There are no sessions to printed\n");
return;
}
session * current_session ;
current_session= head;
while( current_session != NULL)
{
printf("Session :%s\n" , current_session->session_name);
current_session = current_session->next;
}
}
/* add new session */
static int add_session(const char* session_name )
{
// if the current session is NULL //
// then allocate some memory for that //
session* current_session ;
if( head == NULL)
{
head =(session*) malloc( sizeof( session));
head->next =NULL;
current_session = head;
}else{
current_session = head;
while( current_session->next != NULL)
{
current_session = current_session->next;
}
session * new_session = (session*) malloc(sizeof(session));
current_session->next = new_session;
current_session = new_session;
new_session->next = NULL;
}
/*
session* current_session = head;
while( current_session->next != NULL)
{
current_session = current_session->next;
}
// :TODO: some fix is needed here //
if( current_session != head )
{
current_session->next = (session*) malloc( sizeof(session));
current_session = current_session->next;
}
*/
current_session->session_name = strdup( session_name);
current_session->next=NULL;
current_session->head_key_value = NULL;
::current_session = current_session;
/* default return value */
return 0;
}
/* put new keyword value couple into session */
static int put_into_session(session *current_session, \
const char *keyword , const char * value )
{
key_value * current_key_value = current_session->head_key_value;
if(current_key_value != NULL){
while(current_key_value->next != NULL)
{
current_key_value = current_key_value->next;
}
current_key_value->next =(key_value*) malloc( sizeof(key_value));
current_key_value = current_key_value->next;
}
else{
current_session->head_key_value = (key_value*) malloc( sizeof(key_value));
current_key_value = current_session->head_key_value;
}
/* allocate memory to hold string keyword
and copy it here.
*/
int size = strlen(keyword)+1;
current_key_value->key = (char*) malloc(size);
strcpy(current_key_value->key, keyword);
/* allocate memory to hold string value
* and copy it here.
*/
size = strlen(value)+1;
current_key_value->value = (char*) malloc(size);
strcpy(current_key_value->value, value);
current_key_value->next = NULL;
}
/* forward declaration */
static int free_key_value(key_value* /* ptr_key_value */);
/* free memory allocated by the session data
* structure*/
static int free_sessions(session * ptr_session){
// if there are next sessions in the link list
// then
if(ptr_session->next != NULL)
{
free_sessions(ptr_session->next);
}
/* fre the current session */
free( ptr_session->session_name);
free_key_value(ptr_session->head_key_value);
free(ptr_session);
}
/*
* free memory allocated by the key_value pair
* data structure
*/
static int free_key_value ( key_value * ptr_key_value)
{
if( ptr_key_value->next != NULL )
{
/* recursive function would be called */
free_key_value( ptr_key_value->next);
}
/* free the current allocated memory in
* current structure
*/
free( ptr_key_value->key);
free( ptr_key_value->value);
free (ptr_key_value);
}
static char input_buffer[ BUF_SIZE ];
int config_file_open(const char* file_name){
/* check for the prerequisites */
if(!file_name)
{
std::cerr << "ConfigFile: file_name should not be NULL \n";
exit(0);
}
fd=fopen(file_name , "r");
if (fd==NULL)
{
char * error_msg = (char*)malloc(BUF_SIZE);
sprintf( error_msg,"ConfigFile: could not open the file :%s\n",file_name);
std::cerr << error_msg ;
free(error_msg);
exit(0);
}
/* default return */
head = NULL;
current_session = NULL;
return 0;
}
/* de-initialize and free memories associated with.
*
*/
int config_file_dinit()
{
if(head != NULL)
{
// clean up the memory //
free_sessions(head);
}
head = NULL;
current_session =NULL;
fd = NULL;
/* default return value */
return 0;
}
/* parse the configuration file. */
int config_file_parse()
{
if( fd == NULL)
{
std::cerr << "ConfigFile: please call config_file_open() before \
\n calling config_file_parse(). "<< std::endl;
exit(0);
}
int count ;
int state = CONFIG_STATE_START;
int last_state = CONFIG_STATE_START; /* :TODO: update the current state
* in the next increment
* */
char * current_string = (char*) malloc(BUF_SIZE);
char * current_value = (char*) malloc(BUF_SIZE);
char * current_key =(char*) malloc(BUF_SIZE);
while(true){
count = fread(input_buffer,1,BUF_SIZE,fd );
if( count == 0)
break;
int i =0;
while (count > i){
char current_char = input_buffer[i];
i++;
switch (state )
{
case CONFIG_STATE_START:
if(current_char == '[')
{
state = CONFIG_STATE_1;
break;
}
// ignore the white spaces //
if( IS_WHITE_SPACE(current_char) )
{
state=CONFIG_STATE_START;
break;
}
// ignore new lines as well //
if( IS_NEW_LINE(current_char))
{
break;
}
if( IS_ALPHANUM(current_char))
{
state=CONFIG_STATE_3;
i--;
break;
}
if( current_char == 0 )
{
break;
}
else{
std::cerr << "ConfigFile: parsing error have occurrend\
\nLast known state is CONFIG_STATE_START.\n" ;
exit(0);
}
break;
case CONFIG_STATE_1:
// ignore white spaces //
if ( IS_WHITE_SPACE(current_char) )
{
break;
}
if( IS_ALPHANUM(current_char))
{
state = CONFIG_STATE_1;
appendchar(current_string,current_char);
state= CONFIG_STATE_1; // state would not be changed.//
break;
}
if( current_char == '\\' )
{
state = CONFIG_STATE_2;
break;
}
if( current_char == ']')
{
add_session(current_string);
current_string[0] = 0; // again null terminate //
state = CONFIG_STATE_START ;
break;
}
else{
std::cerr <<"ConfigFile: Expected alphanum or / or ]. \n";
exit(0);
}
// ::NEVER REACH::
break;
case CONFIG_STATE_2:
if( IS_WHITE_SPACE(current_char))
{
std::cerr <<"ConfigFile: Not Expected a whitespace after \'\\\'.\n"<<std::endl;
exit(0);
}
if( current_char == 't')
{
appendchar(current_string,'\t');
state = CONFIG_STATE_1;
break;
}
if ( current_char == 's' )
{
appendchar(current_string,' ');
state = CONFIG_STATE_1;
break;
}
if( current_char == 'n')
{
appendchar(current_string, '\n');
state=CONFIG_STATE_1;
break;
}
else{
std::cerr <<"ConfigFile: Expected \\t \\n or \\t.\n" ;
exit(0);
}
// ::NEVER REACH::
break;
case CONFIG_STATE_3:
if(IS_ALPHANUM(current_char) )
{
appendchar(current_string,current_char);
break;
}
if(current_char == '\\')
{
state = CONFIG_STATE_4;
break;
}
if( current_char == '=')
{
strcpy(current_key,current_string);
current_string[0] =0; // bring null termination back to zero position. //
state = CONFIG_STATE_5;
break;
}
else{
std::cerr << "ConfigFile: expected / = or alphanum.\n";
exit(0);
}
// ::NEVER REACH::
break;
case CONFIG_STATE_4:
if( IS_WHITE_SPACE(current_char))
{
std::cerr <<"ConfigFile: Not Expected a whitespace after \'\\\'.\n"<<std::endl;
exit(0);
}
if( current_char == 't')
{
appendchar(current_string,'\t');
state = CONFIG_STATE_3;
break;
}
if ( current_char == 's' )
{
appendchar(current_string,' ');
state = CONFIG_STATE_3;
break;
}
if( current_char == 'n')
{
appendchar(current_string, '\n');
state=CONFIG_STATE_3;
break;
}
else{
std::cerr <<"ConfigFile: Expected \\t \\n or \\t.\n" ;
exit(0);
}
// :NEVER_REACH:
break;
case CONFIG_STATE_5:
/* ignore white spaces */
if( IS_WHITE_SPACE(current_char) )
break;
if(IS_ALPHANUM(current_char) )
{
appendchar(current_string,current_char);
state = CONFIG_STATE_5; // keep in the current state //
break;
}
if( current_char == '\\')
{
state = CONFIG_STATE_6;
break;
}
if( IS_NEW_LINE(current_char) )
{
state = CONFIG_STATE_START;
strcpy(current_value,current_string);
if(current_session ==NULL)
{
std::cerr << "ConfigFile: You need be inside a session before you have key=value pairs.\n";
exit(0);
}
put_into_session(current_session,current_key,current_value);
current_string[0]= 0; // bring the null termination back to zero position //
break;
}
else{
std::cerr << "ConfigFile: expected a newline ,alphanum or '\\' operator.\n";
exit(0);
}
// ::NEVER REACHES:: //
break;
case CONFIG_STATE_6:
if( IS_WHITE_SPACE(current_char))
{
std::cerr <<"ConfigFile: Not Expected a whitespace after \'\\\'.\n"<<std::endl;
exit(0);
}
if( current_char == 't')
{
appendchar(current_string,'\t');
state = CONFIG_STATE_5;
break;
}
if ( current_char == 's' )
{
appendchar(current_string,' ');
state = CONFIG_STATE_5;
break;
}
if( current_char == 'n')
{
appendchar(current_string, '\n');
state=CONFIG_STATE_5;
break;
}
else{
std::cerr <<"ConfigFile: Expected \\t \\n or \\t.\n" ;
exit(0);
}
// :NEVER REACHES: //
break;
default:
std::cerr <<"ConfigFile: Parsing error occurred ! exiting...\n";
exit(0);
}
}
}
// we no longer need these memory //
free(current_string);
free(current_value);
free(current_key);
}
Code: Select all
/* FILE: config_file.h
*/
#ifndef __CONFIG_FILE_H__
#define __CONFIG_FILE_H__
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
/* Internal states that used in state machine */
#define CONFIG_STATE_1 1 /* state1 just after '[' operator from the start state*/
#define CONFIG_STATE_2 2 /* state for the first argument there. */
#define CONFIG_STATE_3 3
#define CONFIG_STATE_4 4
#define CONFIG_STATE_5 5
#define CONFIG_STATE_6 6
#define CONFIG_STATE_7 7
#define CONFIG_STATE_ERROR 100
#define CONFIG_STATE_START 101
/* functions */
int config_file_open(const char * /*file_name*/ );
int config_file_parse();
int config_file_session_into( const char * /* session_name */);
const char* config_file_get_current_session ();
const char* config_file_get_key_value(const char* /*keyword*/ );
int config_file_dinit();
#endif /* ENDIF __CONFIG_FILE_H__ */
To use the code follow this example.
Code: Select all
/*
FILE: example.cpp
demonstrates the usage of config_file
*/
#include "../common.h"
#include "../config_file.h"
#include <stdio.h>
#include <iostream>
const char* file_name ="./config_file_test.ini";
int main(int argc,char** argv)
{
config_file_open(file_name);
config_file_parse();
// session into the main //
if( config_file_session_into("server"))
std::cout << " config_file_session_into success\n";
printf(config_file_get_key_value("keyword1\t\n"));
config_file_get_key_value("keyword3");
return 0;
}
config_file_test.ini
Code: Select all
[main]
keyword1=value1
keyword2=value2
keyword3=value3
[server]
keyword1\t\n=value11\t\t
keyword2=value12
keyword3=value13
Code: Select all
g++ -o config_file.o config_file.cpp
g++ -o example.o example.cpp
g++ -o example config_file.o example.o
./example