/* * 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 #include #include #include #include #include #include #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 . ** 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 , 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 . ** ** Returns: ** a pointer to an English error message with at least ** the lifetime of . */ 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; }