/*-
 *  Copyright (c) 2001  Peter Pentchev
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 */

#include <sys/types.h>

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

#include "penv.h"
#include "pe_compat.h"
#include "pe_log.h"
#include "pe_var.h"

__RINGID("$Ringlet: c/misc/penv/pe_var.c,v 1.9 2004/01/06 16:38:10 roam Exp $");

pe_err_t
pe_var_readfile(struct pe_var *vars, const char *fname, int required) {
	FILE *fp;
	char *line;
	size_t sz;
	pe_err_t r;

	r = PE_ERR_NONE;

	pe_verbose(1, "Reading config file %s\n", fname);
	if (fp = fopen(fname, "rt"), fp == NULL) {
		if ((errno == ENOENT) && !required)
			return (PE_ERR_NONE);
		return (PE_ERR_CFG_OPEN);
	}

	for (;;) {
		if (r = pe_getline(fp, &line, &sz), r)
			return (r);
		if (line == NULL)
			break;
		r = pe_var_parseline(vars, line, sz);
		free(line);
		if (r != PE_ERR_NONE)
			break;
	}
	fclose(fp);
	
	return (r);
}

pe_err_t
pe_var_parseline(struct pe_var *vars, const char *line, size_t sz) {
	size_t n0, n1, v0, v1;
	char *name, *val;
	pe_err_t r;

	if (sz == 0)
		return (PE_ERR_NONE);
	
	for (n0 = 0; n0 < sz; n0++)
		if (!isspace(line[n0]))
			break;
	if ((n0 == sz) || (line[n0] == '#'))
		return (PE_ERR_NONE);
	for (n1 = n0 + 1; n1 < sz; n1++)
		if (isspace(line[n1]) || (line[n1] == '='))
			break;
	if (n1 == sz)
		return (PE_ERR_CFG_SYNTAX);
	for (v0 = n1; v0 < sz; v0++)
		if (!isspace(line[v0]))
			break;
	if ((v0 == sz) || (line[v0] != '='))
		return (PE_ERR_CFG_SYNTAX);
	for (v0++; v0 < sz; v0++)
		if (!isspace(line[v0]))
			break;
	for (v1 = sz; v1 > v0 + 1; v1--)
		if (!isspace(line[v1 - 1]))
			break;
	
	name = calloc(1, n1 - n0 + 1);
	val = calloc(1, v1 - v0 + 1);
	if ((name == NULL) || (val == NULL)) {
		free(name);
		free(val);
		return (PE_ERR_NOMEM);
	}
	memcpy(name, line + n0, n1 - n0);
	memcpy(val, line + v0, v1 - v0);
		
	r = pe_var_set(vars, name, val);

	free(name);
	free(val);
	return (r);
}

pe_err_t
pe_var_set(struct pe_var *vars, const char *name, const char *val) {
	unsigned v;
	unsigned long uval;
	long lval;
	char *end;

	pe_verbose(2, "varset(\"%s\", \"%s\")\n", name, val);

	for (v = 0; vars[v].name != NULL; v++)
		if (!strcmp(vars[v].name, name))
			break;
	if (vars[v].name == NULL)
		return (PE_ERR_CFG_NXVAR);
	if (vars[v].set) {
		pe_verbose(2, " - ignored.\n");
		return (PE_ERR_NONE);
	}

	switch (vars[v].type) {
		case PE_VT_INT:
			lval = strtol(val, &end, 0);
			if ((lval < INT_MIN) || (lval > INT_MAX) ||
			    (*end != '\0'))
				return (PE_ERR_CFG_BADVAL);
			*(unsigned *)vars[v].val = (int) lval;
			break;
			
		case PE_VT_UINT:
			if (val[0] == '-')
				return (PE_ERR_CFG_BADVAL);
			uval = strtoul(val, &end, 0);
			if ((uval > UINT_MAX) ||
			    (*end != '\0'))
				return (PE_ERR_CFG_BADVAL);
			*(unsigned *)vars[v].val = (unsigned) uval;
			break;
			
		case PE_VT_STRING:
			if (strlen(val) > vars[v].size - 1)
				return (PE_ERR_CFG_STRLONG);
			pe_strlcpy((char *)vars[v].val, val, vars[v].size);
			break;

		default:
			return (PE_ERR_INT);
	}
	
	vars[v].set = 1;
	return (PE_ERR_NONE);
}


syntax highlighted by Code2HTML, v. 0.9.1