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