/* $Id: conf.c,v 1.9 2005/11/13 20:49:34 reidrac Exp reidrac $ */

/*
* conf.c, configuration reader and parser
* Copyright (C) 2004, 2005 Juan J. Martinez <jjm*at*usebox*dot*net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<ctype.h>

#include "libmilter/mfapi.h"
#include "conf.h"

#define new_string_list(x) do {\
		x=(struct string_list *) \
			malloc(sizeof(struct string_list));\
		x->n=NULL;\
	} while(0)

static char * pstrncpy(char *, const char *, size_t);
static int parse_string(char *p);
static int parse_qstring(char *p);
static int parse_bool(char *p);
static char * parse_conf(struct conftoken *conf, char *p);

static const char rcsid[]="$Id: conf.c,v 1.9 2005/11/13 20:49:34 reidrac Exp reidrac $";

/*
* strncpy alike function that parses scaped quotes
*/
static char *
pstrncpy(char *d, const char *s, size_t l)
{
	size_t i, j;

	for(i=0, j=0; i<l && s[i]; i++, j++)
	{
		if(s[i]=='\\' && i+1<l)
			if(s[i+1]=='\'' || s[i+1]=='\"')
				i++;
		d[j]=s[i];
	}

	d[j]=0;

	return d;
}

/*
* parses a string and returns its length
*/
static int
parse_string(char *p)
{
	char *t;

	for(t=p; *t && !isspace(*t); t++);

	return (int)(t-p);
}

/*
* parses a quoted string and returns its length
* on error:
*	-1	close quote expected
*	0	empty quotes
*/
static int
parse_qstring(char *p)
{
	char *t;
	char q=*p;

	for(t=++p; *t; t++)
	{
		if(*t==q)
			break;

		if(*t=='\\' && t[1]==q)
			t++;
	}

	if(*t!=q)
		return -1;

	if(t==p)
		return 0;

	return (int)(t-p);
}

/*
* parses a bool and returns 0/1
* all values ne 0 are true
*/
static int
parse_bool(char *p)
{
	if(!p[0] || !isspace(p[1]))
		return -1;

	if(*p=='0')
		return 0;
	else
		return 1;
}

/*
* parses one line each call
*/
static char *
parse_conf(struct conftoken *conf, char *p)
{
	int i, len;
	struct string_list *t;

	if(!p)
		return NULL;

	if(!p[0] || p[0]=='\n' || p[0]=='#')
		return NULL;

	while(isspace(*p))
		p++;

	len=parse_string(p);
	if(!len)
		return NULL;

	for(i=0; conf[i].word; i++)
		if(!strncmp(conf[i].word, p, strlen(conf[i].word))) 
		{
			p+=len;
			while(isspace(*p))
				p++;

			switch(conf[i].required)
			{
				case REQ_NONE:
					/* nothing required */
					break;

				case REQ_BOOL:
					len=parse_bool(p);
					if(len<0)
					{
						fprintf(stderr, 
							"bool expected\n");
						return p;
					}

					conf[i].bool=len;
					p++;
					break;

				case REQ_STRING:
					len=parse_string(p);
					if(!len)
					{
						fprintf(stderr, 
							"string expected\n");
						return p;
					}

					if(conf[i].str)
						free(conf[i].str);
					conf[i].str=(char *)malloc(len+1);
					if(!conf[i].str)
					{
						fprintf(stderr, "malloc\n");
						return p;
					}
					strncpy(conf[i].str, p, len);
					p+=len;
					break;

				case REQ_QSTRING:
					if(*p!='\"' && *p!='\'')
					{
						fprintf(stderr,
							"quoted string"
							" expected\n");
						return p;
					}

					len=parse_qstring(p);
					p++;

					if(len==-1)
					{
						fprintf(stderr, 
							"end quote expected\n");
						return p;
					}

					if(!len)
					{
						fprintf(stderr, 
							"empty quotes\n");
						return p;
					}

					if(conf[i].str)
						free(conf[i].str);
					conf[i].str=(char *)malloc(len+1);
					if(!conf[i].str)
					{
						fprintf(stderr, "malloc\n");
						return p;
					}
					pstrncpy(conf[i].str, p, len);
					p+=len+1;
					break;

				case REQ_LSTQSTRING:
					if(*p!='\"' && *p!='\'')
					{
						fprintf(stderr,
							"quoted string"
							" expected\n");
						return p;
					}

					len=parse_qstring(p);
					p++;

					if(len==-1)
					{
						fprintf(stderr, 
							"end quote expected\n");
						return p;
					}

					if(!len)
					{
						fprintf(stderr, 
							"empty quotes\n");
						return p;
					}

					if(!conf[i].sl)
					{
						new_string_list(conf[i].sl);
						if(!conf[i].sl)
						{
							fprintf(stderr,
								"malloc");
							return p;
						}
					}
					else
					{
						new_string_list(t);
						if(!t)
						{
							fprintf(stderr,
								"malloc");
							return p;
						}
						t->n=conf[i].sl;
						conf[i].sl=t;
					}

					conf[i].sl->s=(char *)malloc(len+1);
					if(!conf[i].sl->s)
					{
						fprintf(stderr, "malloc\n");
						return p;
					}
					pstrncpy(conf[i].sl->s, p, len);
					p+=len+1;
					break;
			}
			break;
		}

	if(conf[i].word)
	{
		while(isspace(*p))
			p++;

		if(!p[0])
			return NULL;
	
		fprintf(stderr, "parse error\n");
		return p;
	}

	fprintf(stderr, "unknown token\n");

	p[len]=0;
	return p;
}

/*
* reads and parses the configuration file
*/
int
read_conf(const char *filename, struct conftoken *conf)
{
	FILE *fd;
	char buffer[1024];
	char *ret;
	int line, i;

	fd=fopen(filename, "r");
	if(!fd)
		return 0;

	line=1;
	while(!feof(fd))
	{
		i=0;
		do
		{
			if(i>1023)
			{
				fclose(fd);
				fprintf(stderr, "conf line %i is too long\n",
					 line);
				return 1;
			}

			fscanf(fd, "%c", &buffer[i]);

		} while(buffer[i++]!='\n' && !feof(fd));

		buffer[i]=0;

		ret=parse_conf(conf, buffer);
		if(ret)
		{
			fclose(fd);
			fprintf(stderr, "conf error at line %i, near: %s\n",
				line, ret);
			return 1;
		}

		line++;
	}

	fclose(fd);

	return 0;
}

/* EOF */


syntax highlighted by Code2HTML, v. 0.9.1