/*
* Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: sm-conf.c,v 1.19 2007/05/20 15:41:56 ca Exp $")
#if SM_LIBCONF_ALONE
#include <limits.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include "sm-conf.h"
#include "sm-util.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/limits.h"
#include "sm/string.h"
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/ctype.h"
#include "sm/heap.h"
#include "sm/memops.h"
#include "sm/sm-conf.h"
#endif /* SM_LIBCONF_ALONE */
#include "sm-conf-state.h"
#include "sm-conf-token.h"
#include "sm-conf-parse.h"
/* SM-CONF.C -- high-level API functions */
/*
** SM_CONF_NEW -- create a new configuration state
**
** The configuration state is passed into all other
** configuration-related functions.
**
** Parameters:
** name -- name used for error messages
**
** Returns:
** NULL on allocation errors,
** otherwise a pointer to a new sm_conf_T that must
** be free'd using sm_conf_destroy().
*/
sm_conf_T *
sm_conf_new(char const *name)
{
size_t name_n;
sm_conf_T *smc;
name_n = (NULL == name ? 0 : strlen(name) + 1);
if ((smc = sm_zalloc(sizeof(*smc) + name_n)) != NULL) {
if (0 == name_n)
smc->smc_name = NULL;
else
smc->smc_name = sm_memcpy((char *)(smc + 1), name, name_n);
smc->sm_magic = SM_CONF_MAGIC;
smc->smc_line = 1;
smc->smc_error_tail = &smc->smc_error_head;
}
return smc;
}
/*
** SM_CONF_READ_DATA -- read configuration text from a data pointer
**
** Parameters:
** smc -- configuration state with a read configuration file
** data -- bytes to read
** n -- number of bytes pointed to by <data>.
** copy -- copy data or just point to it?
**
** Returns:
** 0 on success,
** otherwise an errno or SM_CONF_ERR_... error number.
*/
int
sm_conf_read_data(sm_conf_T *smc, char *data, size_t n, bool copy)
{
if (NULL == smc || (data == NULL && n != 0))
return SM_CONF_ERR_INVALID;
SM_IS_CONF(smc);
if (n > 0 && copy) {
SM_REQUIRE(data != NULL);
smc->smc_buf = sm_malloc(n);
if (NULL == smc->smc_buf)
return ENOMEM;
smc->smc_mybuf = true;
sm_memcpy(smc->smc_buf, data, n);
}
else if (!copy) {
smc->smc_buf = data;
smc->smc_mybuf = false;
}
smc->smc_buf_n = n;
return sm_conf_parse(smc);
}
/*
** SM_CONF_DESTROY -- free configuration resources
**
** Given a handle returned by <sm_conf_new()>, this call
** frees all configuration resources associated with that handle.
**
** Parameters:
** smc -- NULL or configuration handle to free
**
** Returns:
** none
*/
void
sm_conf_destroy(sm_conf_T *smc)
{
if (smc != NULL) {
sm_conf_error_T *e;
/* Free stored syntax errors */
while ((e = smc->smc_error_head) != NULL) {
smc->smc_error_head = e->sce_next;
sm_free(e);
}
if (smc->smc_mybuf) {
SM_FREE(smc->smc_buf);
smc->smc_mybuf = false;
}
sm_conf_node_destroy(smc, smc->smc_root);
smc->sm_magic = SM_MAGIC_NULL;
sm_free(smc);
}
}
/*
** SM_CONF_STRERROR -- return English string for error message
**
** Parameters:
** err -- error number returned by an sm_conf_* function
** buf -- buffer to use in assembling the error message
** bufsize -- # of bytes pointed to by <buf>.
**
** Returns:
** a pointer to an English error message with at least
** the lifetime of <buf>.
*/
char const *
sm_conf_strerror(int err, char *buf, size_t bufsize)
{
switch (err) {
case SM_CONF_ERR_NEWLINE_IN_STRING:
return "newline in string";
case SM_CONF_ERR_EOF_IN_STRING:
return "EOF in string";
case SM_CONF_ERR_BAD_CHAR:
return "unexpected character";
case SM_CONF_ERR_NO_MEMORY:
return "out of memory";
case SM_CONF_ERR_HEX_EXPECTED:
return "hexadecimal digit expected after \\x, \\u or \\U";
case SM_CONF_ERR_CHAR_OVERFLOW:
return "character constant too large";
case SM_CONF_ERR_INVALID:
return "invalid argument to configuration function";
case SM_CONF_ERR_READ:
return "error while reading configuration file";
case SM_CONF_ERR_READ_OPEN:
return "cannot open configuration file for reading";
case SM_CONF_ERR_READ_CLOSE:
return "error closing configuration file after reading";
case SM_CONF_ERR_SYNTAX:
return "syntax error";
case SM_CONF_ERR_NOT_FOUND:
return "not found";
case SM_CONF_ERR_ALREADY:
return "already exists";
case SM_CONF_ERR_TYPE:
return "invalid value";
case SM_CONF_ERR_NUL_IN_STRING:
return "ascii NUL character in string";
case SM_CONF_ERR_TOO_MANY:
return "too many elements in array";
default:
break;
}
if (err > 0)
return strerror(err);
snprintf(buf, bufsize,
"unexpected configuration parser error %d", err);
return buf;
}
/*
** SM_CONF_SYNTAX_ERROR -- return the next syntax error.
**
** Parameters:
** smc -- handle
** prev -- NULL or the previous error
**
** Returns:
** a pointer to an English error message or NULL,
** if we're out of errors.
*/
char const *
sm_conf_syntax_error(sm_conf_T *smc, char const *prev)
{
sm_conf_error_T const *e;
if (NULL == smc)
return NULL == prev ? "initialization failed" : NULL;
SM_IS_CONF(smc);
if (NULL == prev) {
if (NULL == smc->smc_error_head)
return NULL;
return smc->smc_error_head->sce_text;
}
/*
** prev points to previous error message. To get back to the previous
** sm_conf_error_T we need to subtract the offset of sce_text.
*/
e = (sm_conf_error_T const *)
((char const *)prev - offsetof(sm_conf_error_T, sce_text));
if (NULL == e->sce_next)
return NULL;
return e->sce_next->sce_text;
}
/*
** SM_CONF_ERROR_ADD -- add an error statement
**
** Parameters:
** smc -- handle
** fmt -- format
** ... -- arguments for fmt
**
** Returns:
** error code
**
** Side Effects:
** allocates new error context and adds it to smc error list.
** prints formatted text into error text field of error context.
*/
int
sm_conf_error_add(sm_conf_T *smc, char const *fmt, ...)
{
va_list ap;
sm_conf_error_T *err;
SM_IS_CONF(smc);
err = sm_malloc(sizeof(*err));
if (NULL == err)
return ENOMEM;
va_start(ap, fmt);
vsnprintf(err->sce_text, sizeof err->sce_text, fmt, ap);
err->sce_next = NULL;
va_end(ap);
*smc->smc_error_tail = err;
smc->smc_error_tail = &err->sce_next;
smc->smc_error_n++;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1