/*
** CONFIG FILE: Simple config file parser
** Copyright (C) 2002 Michael W. Shaffer <mwshaffer@angrypot.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU 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 General Public License for more details.  
**
** You should have received a copy of the GNU General Public License
** along with this program (see the file COPYING). If not, write to:
**
** The Free Software Foundation, Inc.
** 59 Temple Place, Suite 330,
** Boston, MA  02111-1307  USA
*/

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "config_file.h"

/*
** Config file format is as follows:
**
**  - settings are in the form of either:
**
**      key = value
**
**    or:
**
**      key : value
**
**  - keys and values may contain only the characters:
**
**      - _ . / + a-z A-Z 0-9 
**
**  - whitespace is optional and is discarded along with anything
**    else no explicitly listed above
**  - comments start with either ';' or '#' and continue
**    until end of line
**  - only one 'setting' per line
**
** The parsing routine basically supports either the classic
** unix style conf file with ':' as key/value separator and 
** '#' as comment character as well as .ini like syntax with
** '=' as key/value separator and ';' as comment character. I
** don't think that real windows .ini files allow comments on
** the same line as settings, but this routine does.
**
** Here are some short samples of each format:
**
*******************************************************************************
**
**  #
**  # sample exampled.conf file
**  # (classic unix syntax)
**  #
**  
**  host: 127.0.0.1          # peer host to monitor
**  interval: 20             # interval in seconds between checks
**  tolerance: 3             # number of failed checks to initiate failover
**
*******************************************************************************
**
**  ;
**  ; sample exampled.conf file
**  ; (.ini like systax)
**  ;
**  
**  host = 127.0.0.1          ; peer host to monitor
**  interval = 20             ; interval in seconds between checks
**  tolerance = 3             ; number of failed checks to initiate failover
** 
*******************************************************************************
*/

enum {
	tagsize = 8192,
	bufsize = 8192
};

enum character_classes {
	c_junk = 0,
	c_name_char,
	c_separator,
	c_end_of_line,
	c_comment
};

enum parse_states {
	s_comment = 0,
	s_key,
	s_value
};

static int parse_state = s_key;

static int classify (char c)
{
	if ((parse_state == s_comment) && !((c == '\n') || (c == '\r')))
		return c_comment;

	if ((c >= 'a') && (c <= 'z'))
		return c_name_char;
	if ((c >= 'A') && (c <= 'Z'))
		return c_name_char;
	if ((c >= '0') && (c <= '9'))
		return c_name_char;

	switch (c) {
		case '-':
		case '_':
		case '.':
		case '/':
		case '+':
			return c_name_char;
		case ':':
		case '=':
		case '|':
			parse_state = s_value;
			return c_separator;
		case ';':
		case '#':
			parse_state = s_comment;
			return c_comment;
		case '\n':
		case '\r':
			parse_state = s_key;
			return c_end_of_line;
		default:
			return c_junk;

	}
}

int parse_config_file (char *conf_path, struct hash_table *conf)
{
	int fd = -1;
	char *buf = NULL;
	char *bufp = NULL;
	struct datum d;
	char c = '\0';
	char key[tagsize];
	char val[tagsize];
	int kpos = 0;
	int vpos = 0;

	if ( !(conf_path && conf))
		return -1;

	fd = open (conf_path, O_RDONLY);
	if (fd == -1) {
		return -1;
	}

	buf = (char *) malloc (bufsize);
	if (!buf) {
		return -1;
	}

	parse_state = s_key;
	memset (key, 0, sizeof (key));
	memset (val, 0, sizeof (val));
	memset (&d, 0, sizeof (struct datum));
	d.key = key;
	d.val = val;

	memset (buf, 0, bufsize);
	while (read (fd, buf, (bufsize - 1)) > 0) {
		bufp = buf;
		while ((c = *(bufp++))) {
			switch (classify (c)) {
				case c_name_char:
					if (parse_state == s_key) {
						if (kpos < (tagsize - 1))
							key[kpos++] = c;
					} else if (parse_state == s_value) {
						if (vpos < (tagsize - 1))
							val[vpos++] = c;
					}
					break;
				case c_end_of_line:
					if (strlen (key) > 0) {
						d.ksize = strlen (key);
						d.vsize = strlen (val);
						hash_table_insert (conf, &d);
					}
					memset (key, 0, sizeof (key));
					memset (val, 0, sizeof (val));
					kpos = 0;
					vpos = 0;
					break;
				default:
					break;
			}
		}
		memset (buf, 0, bufsize);
	}

	if (buf)
		free (buf);
	if (fd > 0)
		close (fd);
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1