/*
** Copyright (c) 2006, 2007 Sendmail, Inc. and its suppliers.
** All rights reserved.
**
** $Id: config.c,v 1.5 2007/10/22 01:28:31 msk Exp $
*/
#ifndef lint
static char config_c_id[] = "@(#)$Id: config.c,v 1.5 2007/10/22 01:28:31 msk Exp $";
#endif /* !lint */
/* system includes */
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* dkim-filter includes */
#include "config.h"
/*
** CONFIG_FREE -- release memory associated with a config list
**
** Parameters:
** head -- head of the config list
**
** Return value:
** None.
*/
void
config_free(struct config *head)
{
struct config *next;
struct config *cur;
cur = head;
while (cur != NULL)
{
next = cur->cfg_next;
free(cur);
cur = next;
}
}
/*
** CONFIG_LOAD -- load configuration from a file
**
** Parameters:
** in -- stream from which to load
** cd -- array of (struct configdef) elements containing the
** configuration syntax to assert
** line -- line number where an error occurred (updated)
**
** Return value:
** Pointer to a (struct config) which is the head of a list of
** loaded configuration items, or NULL on error; if NULL, "line" is
** updated to indicate which line number contained the error.
*/
struct config *
config_load(FILE *in, struct configdef *def, unsigned int *line)
{
int arg;
int n = -1;
int err = 0;
unsigned int myline = 0;
int value = -1;
char *p;
char *str = NULL;
struct config *new;
struct config *cur = NULL;
char buf[BUFRSZ + 1];
assert(in != NULL);
assert(def != NULL);
memset(buf, '\0', sizeof buf);
while (fgets(buf, sizeof buf - 1, in) != NULL)
{
myline++;
/* read a line; truncate at newline or "#" */
for (p = buf; *p != '\0'; p++)
{
if (*p == '#' || *p == '\n')
{
*p = '\0';
break;
}
}
arg = 0;
/* break down the arguments */
/* XXX -- need something better than strtok(), for quoting */
for (p = strtok(buf, " \t");
err == 0 && p != NULL;
p = strtok(NULL, " \t"))
{
/* recognize the first? */
if (arg == 0)
{
for (n = 0; ; n++)
{
/* nope */
if (def[n].cd_name == NULL)
{
err = 1;
break;
}
if (strcasecmp(def[n].cd_name, p) == 0)
break;
}
}
else
{
char *q;
switch (def[n].cd_type)
{
case CONFIG_TYPE_STRING:
str = p;
break;
case CONFIG_TYPE_BOOLEAN:
if (p[0] == 't' ||
p[0] == 'T' ||
p[0] == 'y' ||
p[0] == 'Y' ||
p[0] == '1')
value = 1;
else if (p[0] == 'f' ||
p[0] == 'F' ||
p[0] == 'n' ||
p[0] == 'N' ||
p[0] == '0')
value = 0;
else
err = 1;
break;
case CONFIG_TYPE_INTEGER:
value = (int) strtol(p, &q, 0);
if (*q != '\0')
err = 1;
break;
default:
assert(0);
/* NOTREACHED */
return NULL;
}
}
arg++;
}
/* no arguments */
if (arg == 0)
continue;
/* a parse error, or only one argument, is no good */
if (arg == 1 || err == 1)
{
config_free(cur);
if (line != NULL)
*line = myline;
return NULL;
}
new = (struct config *) malloc(sizeof(struct config));
if (new == NULL)
{
config_free(cur);
if (line != NULL)
*line = myline;
return NULL;
}
new->cfg_next = cur;
new->cfg_name = def[n].cd_name;
new->cfg_type = def[n].cd_type;
switch (new->cfg_type)
{
case CONFIG_TYPE_STRING:
new->cfg_string = strdup(str);
break;
case CONFIG_TYPE_BOOLEAN:
new->cfg_bool = (bool) value;
break;
case CONFIG_TYPE_INTEGER:
new->cfg_int = value;
break;
default:
assert(0);
}
cur = new;
}
return cur;
}
/*
** CONFIG_CHECK -- verify that stuff marked "required" is present
**
** Parameters:
** head -- head of config list
** def -- definitions
**
** Return value:
** Name of the first parameter in "def" that was marked "required"
** yet absent from the configuration parsed, or NULL if nothing
** required was missing.
*/
char *
config_check(struct config *head, struct configdef *def)
{
int n;
struct config *cur;
assert(head != NULL);
assert(def != NULL);
for (n = 0; ; n++)
{
if (def[n].cd_name == NULL)
return NULL;
if (!def[n].cd_req)
continue;
for (cur = head; cur != NULL; cur = cur->cfg_next)
{
if (cur->cfg_name == def[n].cd_name)
break;
}
if (cur == NULL)
return def[n].cd_name;
}
/* NOTREACHED */
}
/*
** CONFIG_GET -- retrieve a parameter's value
**
** Parameter:
** head -- head of config list
** name -- name of the parameter of interest
** value -- where to write the result (returned)
** size -- bytes available at "value"
**
** Return value:
** 1 if the data was found, 0 otherwise, -1 if the request was illegal
**
** Notes:
** "value" is a (void *). It can be used directly, such as:
**
** int x;
**
** (void) config_get(conflist, "MyInteger", (void *) &x);
*/
int
config_get(struct config *head, const char *name, void *value, size_t size)
{
struct config *cur;
assert(head != NULL);
assert(name != NULL);
assert(value != NULL);
assert(size > 0);
for (cur = head; cur != NULL; cur = cur->cfg_next)
{
if (strcasecmp(cur->cfg_name, name) == 0)
{
switch (cur->cfg_type)
{
case CONFIG_TYPE_BOOLEAN:
if (size != sizeof(bool))
return -1;
memcpy(value, &cur->cfg_bool, size);
break;
case CONFIG_TYPE_INTEGER:
if (size != sizeof(int))
return -1;
memcpy(value, &cur->cfg_int, size);
break;
default:
if (size != sizeof(char *))
return -1;
memcpy(value, &cur->cfg_string, size);
break;
}
return 1;
}
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1