/*
* Copyright (c) 2004 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-node.c,v 1.14 2005/06/16 20:35:10 ca Exp $")
#if SM_LIBCONF_ALONE
#include <errno.h>
#include <string.h>
#include "sm-util.h"
#include "sm-conf-util.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/string.h"
#include "sm/heap.h"
#endif /* SM_LIBCONF_ALONE */
#include "sm-conf-state.h"
#include "sm-conf-node.h"
#include "sm-conf-token.h"
#include "sm-conf-type.h"
/* SM-CONF-NODE.C -- node access routines. */
#define SM_CONF_NODE_MAGIC SM_MAGIC('C', 'N', 'O', 'D')
#define SM_IS_CONF_NODE(sm_conf_node) \
SM_REQUIRE((sm_conf_node) != NULL && \
(sm_conf_node)->sm_magic == SM_CONF_NODE_MAGIC)
typedef struct sm_conf_node_cache_S
{
struct sm_conf_node_cache_S *scnc_next;
void *scnc_data;
sm_conf_definition_T const *scnc_definition;
void (*scnc_free)(
union sm_conf_node_U *,
void *,
sm_conf_definition_T const *);
} sm_conf_node_cache_T;
/*
** (ca) use queue.h macros for lists?
*/
union sm_conf_node_U
{
sm_magic_T sm_magic;
#define scn_type scn_generic.scng_type
struct
{
sm_magic_T sm_magic;
/* SCNG_TYPE -- distinguish between node, list, or section. */
enum sm_conf_node_type_E scng_type;
/*
** SCNG_NEXT
** SCNG_PARENT -- list or section elements have these.
** (next points to the next element in the list or
** section; parent to the container node.)
**
** Only the root node has a NULL parent.
*/
union sm_conf_node_U *scng_next;
union sm_conf_node_U *scng_parent;
/*
** SCNG_SLOT
** SCNG_SLOT_N -- section elements can have this
** if they're named (i.e., if this is a named
** property in a section rather than a subsection.)
**
*/
char const *scng_slot;
size_t scng_slot_n;
/* SCNG_TOKEN -- the first input token; has line number. */
sm_conf_token_T scng_token;
/* SCNG_CACHE -- temporary memory for value conversion. */
sm_conf_node_cache_T *scng_cache;
} scn_generic;
struct
{
sm_magic_T sm_magic;
enum sm_conf_node_type_E scnv_type;
union sm_conf_node_U *scnv_next;
union sm_conf_node_U *scnv_parent;
char const *scnv_slot;
size_t scnv_slot_n;
sm_conf_token_T scnv_token;
sm_conf_node_cache_T *scnv_cache;
char const *scnv_text;
size_t scnv_text_n;
} scn_value;
struct
{
sm_magic_T sm_magic;
enum sm_conf_node_type_E scnl_type;
union sm_conf_node_U *scnl_next;
union sm_conf_node_U *scnl_parent;
char const *scnl_slot;
size_t scnl_slot_n;
sm_conf_token_T scnl_token;
sm_conf_node_cache_T *scnl_cache;
/*
** SCNL_HEAD -- first element of the list.
** SCNL_TAIL -- address of last element of the list.
** SCNL_N -- # of subelements.
*/
union sm_conf_node_U *scnl_head;
union sm_conf_node_U **scnl_tail;
size_t scnl_n;
} scn_list;
struct
{
sm_magic_T sm_magic;
enum sm_conf_node_type_E scns_type;
union sm_conf_node_U *scns_next;
union sm_conf_node_U *scns_parent;
char const *scns_slot;
size_t scns_slot_n;
sm_conf_token_T scns_token;
sm_conf_node_cache_T *scns_cache;
/*
** SCNS_KEYWORD
** SCNS_KEYWORD_N -- (unquoted) type of a section.
** In
** foo "bar" {
** ...
** }
** <foo> is the section keyword.
*/
char const *scns_keyword;
size_t scns_keyword_n;
/*
** SCNS_NAME
** SCNS_NAME_N -- (quoted) name of a section.
** In
** foo "bar" {
** ...
** }
** <bar> is the section name.
** Syntactically, section names are optional.
*/
char const *scns_name;
size_t scns_name_n;
/*
** SCNS_HEAD -- first element of the section.
** SCNS_TAIL -- address of last element of the section.
** SCNS_N -- # of subelements.
*/
sm_conf_node_T *scns_head;
sm_conf_node_T **scns_tail;
size_t scns_n;
} scn_section;
};
/*
** SM_CONF_NODE_CACHE_GET -- allocate a per-node cache structure.
**
** Called by the to-value conversion of the definition.
**
** Parameters:
** node -- node for which we're allocating a cache
** size -- # of bytes to allocate
** def -- definition that causes the cache to be allocated,
** or NULL
** free_cache -- callback to call when this is free'ed,
** or NULL
** result -- assign result code to this.
**
** Returns:
** pointer to the allocated or existing data,
** or NULL if an allocation failed.
*/
void *
sm_conf_node_cache_get(
sm_conf_node_T *node,
size_t size,
sm_conf_definition_T const *def,
void (*free_cache)(
sm_conf_node_T *,
void *,
sm_conf_definition_T const *),
int *result)
{
sm_conf_node_cache_T *ca;
if (node == NULL)
{
*result = EINVAL;
return NULL;
}
/*
** If we find an existing cache with the same definition,
** just reuse that.
*/
for (ca = node->scn_list.scnl_cache;
ca != NULL;
ca = ca->scnc_next)
if (ca->scnc_definition == def)
{
*result = EEXIST;
return ca->scnc_data;
}
if ((ca = sm_zalloc(sizeof(*ca))) == NULL)
{
*result = ENOMEM;
return NULL;
}
ca->scnc_data = sm_zalloc(size == 0 ? 1 : size);
if (ca->scnc_data == NULL)
{
*result = ENOMEM;
sm_free(ca);
return NULL;
}
*result = 0;
ca->scnc_next = node->scn_list.scnl_cache;
node->scn_list.scnl_cache = ca;
ca->scnc_definition = def;
ca->scnc_free = free_cache;
return ca->scnc_data;
}
/*
** SM_CONF_NODE_CACHE_FREE -- free per-node cache structures.
**
** Parameters:
** node -- node whose cache we're freeing
**
** Returns:
** none
*/
static void
sm_conf_node_cache_free(sm_conf_node_T *node)
{
sm_conf_node_cache_T *ca;
while ((ca = node->scn_list.scnl_cache) != NULL)
{
node->scn_list.scnl_cache = ca->scnc_next;
if (ca->scnc_free != NULL)
(* ca->scnc_free)(
node, ca->scnc_data, ca->scnc_definition);
sm_free(ca->scnc_data);
sm_free(ca);
}
}
/*
** SM_CONF_NODE_NEW -- allocate a new node
**
** Parameters:
** smc -- context handle
** token -- token that started that node.
**
** Returns:
** a node pointer on success, NULL on error.
**
** Last code review: 2004-05-17 16:14:28; see comments
** Last code change:
*/
static sm_conf_node_T *
sm_conf_node_new(sm_conf_T *smc, sm_conf_token_T *token)
{
sm_conf_node_T *n;
SM_IS_CONF(smc);
if (token != NULL)
SM_IS_CONF_TOKEN(token);
if ((n = sm_zalloc(sizeof(*n))) != NULL)
{
n->sm_magic = SM_CONF_NODE_MAGIC;
n->scn_generic.scng_slot = NULL;
n->scn_generic.scng_slot_n = 0;
n->scn_generic.scng_next = NULL;
if (token != NULL)
n->scn_generic.scng_token = *token;
else
n->scn_generic.scng_token.sct_type = SM_CONF_TOKEN_NONE;
}
return n;
}
/*
** SM_CONF_VALUE_NEW -- allocate a new single-element value
**
** Parameters:
** smc -- context handle
** text -- the (unquoted) spelling of the value data.
** text_n -- number of bytes pointed to by <text>
** token -- origin of the data.
**
** Returns:
** a node pointer on success, NULL on error.
**
** Last code review: 2004-05-17 16:14:52; see comments!
** Last code change: 2004-05-20 21:02:49 jutta
*/
sm_conf_node_T *
sm_conf_value_new(
sm_conf_T *smc,
char const *text,
size_t text_n,
sm_conf_token_T *token)
{
sm_conf_node_T *n;
SM_IS_CONF(smc);
if (token != NULL)
SM_IS_CONF_TOKEN(token);
if ((n = sm_conf_node_new(smc, token)) != NULL)
{
n->scn_value.scnv_type = SM_CONF_NODE_VALUE;
if (text != NULL)
{
n->scn_value.scnv_text = text;
n->scn_value.scnv_text_n = text_n;
}
else if (token != NULL)
{
n->scn_value.scnv_text = token->sct_text;
n->scn_value.scnv_text_n = token->sct_text_n;
}
else
{
n->scn_value.scnv_text = NULL;
n->scn_value.scnv_text_n = 0;
}
}
return n;
}
/*
** SM_CONF_VALUE -- get the text of a value node.
**
** Parameters:
** smc -- context handle
** node -- the node
** text_out -- out: the (unquoted) spelling of the value data.
** text_n_out -- out: number of bytes pointed to by <text>
**
** Returns:
** 0 on success, SM_CONF_ERR_INVALID on error.
**
** Last code review: 2004-05-17 16:17:26
** Last code change: 2004-05-20 21:04:48
*/
int
sm_conf_value(
sm_conf_T *smc,
sm_conf_node_T const *node,
char const **text_out,
size_t *text_n_out)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
if ( node == NULL
|| node->scn_generic.scng_type != SM_CONF_NODE_VALUE)
return SM_CONF_ERR_INVALID;
if (text_out != NULL)
*text_out = node->scn_value.scnv_text;
if (text_n_out != NULL)
*text_n_out = node->scn_value.scnv_text_n;
return 0;
}
/*
** SM_CONF_LIST_NEW -- allocate a new list object
**
** Parameters:
** smc -- context handle
**
** Returns:
** a node pointer on success, NULL on error.
**
** Last code review: 2004-05-17 16:18:11
** Last code change: 2004-05-20 21:04:38
*/
sm_conf_node_T *
sm_conf_list_new(sm_conf_T *smc, sm_conf_token_T *token)
{
sm_conf_node_T *n;
SM_IS_CONF(smc);
if (token != NULL)
SM_IS_CONF_TOKEN(token);
if ((n = sm_conf_node_new(smc, token)) != NULL)
{
n->scn_list.scnl_type = SM_CONF_NODE_LIST;
n->scn_list.scnl_head = NULL;
n->scn_list.scnl_tail = &n->scn_list.scnl_head;
n->scn_list.scnl_n = 0;
}
return n;
}
/*
** SM_CONF_LIST_APPEND -- append to a list
**
** Parameters:
** smc -- context handle
** list -- list to append to, must be non-NULL
** node -- node or node chain to append.
**
** Returns:
** none
**
** Last code review: 2004-05-17 16:23:50, see comments
** Last code change: 2004-05-20 21:05:53
*/
void
sm_conf_list_append(sm_conf_T *smc, sm_conf_node_T *list, sm_conf_node_T *node)
{
SM_IS_CONF(smc);
if (node == NULL)
return;
SM_REQUIRE(list != NULL);
SM_IS_CONF_NODE(node);
SM_IS_CONF_NODE(list);
*list->scn_list.scnl_tail = node;
while (node->scn_generic.scng_next != NULL)
node = node->scn_generic.scng_next;
list->scn_list.scnl_tail = &node->scn_generic.scng_next;
list->scn_list.scnl_n++;
node->scn_generic.scng_parent = list;
}
/*
** SM_CONF_LIST_NEXT -- get next element from a list
**
** Parameters:
** smc -- context handle
** list -- list to get the next element of
** prev -- NULL or predecessor
**
** Returns:
** NULL if the list is NULL or not a list.
** if <prev> is NULL, the list's first element
** if <prev> is non-NULL, <prev>'s successor element.
**
** Last code review: 2004-05-17 16:24:46, see comments
** Last code change: 2004-05-20 21:11:17 jutta
*/
sm_conf_node_T *
sm_conf_list_next(
sm_conf_T *smc,
sm_conf_node_T const *list,
sm_conf_node_T const *prev)
{
SM_IS_CONF(smc);
if (list != NULL)
SM_IS_CONF_NODE(list);
if (prev != NULL)
SM_IS_CONF_NODE(prev);
if (list == NULL || list->scn_type != SM_CONF_NODE_LIST)
return NULL;
return prev == NULL
? list->scn_list.scnl_head
: prev->scn_generic.scng_next;
}
/*
** SM_CONF_LIST_N -- get # of entries of a list.
**
** Parameters:
** smc -- context handle
** list -- NULL or a node pointer
**
** Returns:
** 0 if this isn't a list, otherwise the number
** of entries.
**
** Last code review: 2004-05-17 16:28:13, see comments
** Last code change: 2004-05-20 21:12:36 jutta
*/
size_t
sm_conf_list_n(sm_conf_T *smc, sm_conf_node_T const *list)
{
SM_IS_CONF(smc);
if (list != NULL)
SM_IS_CONF_NODE(list);
if (list == NULL || list->scn_type != SM_CONF_NODE_LIST)
return 0;
return list->scn_list.scnl_n;
}
/*
** SM_CONF_LIST_ARGV -- get a vector of a list's textual subentries
**
** Parameters:
** smc -- context handle
** list -- NULL or a node
** argv_out -- NULL or a location where the argv value
** should be stored.
** Returns:
** 0 on success,
** SM_CONF_ERR_NUL_IN_STRING -- at least one of the
** values in the list contains a NUL byte and
** cannot be represented as part of a string vector
** SM_CONF_ERR_TYPE -- at least one of the values in the
** list is not a single value (but another list),
** SM_CONF_ERR_INVALID -- that wasn't a list or value.
** other error codes (e.g., allocation errors)
**
** Last code review: 2004-05-17 16:41:02, see comments
** Last code change: 2004-05-20 21:11:03 jutta
*/
int
sm_conf_node_argv(
sm_conf_T *smc,
sm_conf_node_T *list,
char const * const **argv_out)
{
int result;
char const **argv;
SM_IS_CONF(smc);
if (list == NULL)
{
if (argv_out != NULL)
*argv_out = NULL;
return 0;
}
SM_IS_CONF_NODE(list);
if (list->scn_type == SM_CONF_NODE_VALUE)
{
if ( strlen(list->scn_value.scnv_text)
!= list->scn_value.scnv_text_n)
return SM_CONF_ERR_NUL_IN_STRING;
/* Make a two-element vector. */
argv = sm_conf_node_cache_get(list, 2 * sizeof(char *),
NULL, NULL, &result);
if (argv == NULL)
return SM_CONF_ERR_NO_MEMORY;
/* was this actually new? */
if (result == 0)
{
argv[0] = list->scn_value.scnv_text;
argv[1] = NULL;
}
}
else if (list->scn_type == SM_CONF_NODE_LIST)
{
sm_conf_node_T *n;
size_t i;
/* Do these list elements fit into an argv-style vector? */
for (n = list->scn_list.scnl_head, i = 0;
n != NULL && i <= list->scn_list.scnl_n;
n = n->scn_generic.scng_next, i++)
{
if (n->scn_type != SM_CONF_NODE_VALUE)
return SM_CONF_ERR_TYPE;
if ( strlen(n->scn_value.scnv_text)
!= n->scn_value.scnv_text_n)
return SM_CONF_ERR_NUL_IN_STRING;
}
/* Yes. Obtain or allocate one. */
argv = sm_conf_node_cache_get(list,
(list->scn_list.scnl_n + 1) * sizeof(char *),
NULL, NULL, &result);
if (argv == NULL)
return SM_CONF_ERR_NO_MEMORY;
/* was this actually new? */
if (result == 0)
{
for (n = list->scn_list.scnl_head, i = 0;
n != NULL && i <= list->scn_list.scnl_n;
n = n->scn_generic.scng_next, i++)
argv[i] = n->scn_value.scnv_text;
SM_ASSERT(i <= list->scn_list.scnl_n);
argv[i] = NULL;
}
}
else
return SM_CONF_ERR_INVALID;
if (argv_out != NULL)
*argv_out = argv;
return 0;
}
/*
** SM_CONF_SECTION_NEW -- allocate a new section object
**
** A section is an optionally named hashtable ((ca): list right now)
**
** Parameters:
** smc -- context handle
** name -- name of the section, or NULL
** name_n -- # of bytes pointed to by <name>.
** token -- NULL or first token of the section.
**
** Returns:
** pointer to new node, NULL on failure
**
** Last code review:
** Last code change:
*/
sm_conf_node_T *
sm_conf_section_new(
sm_conf_T *smc,
char const *keyword,
size_t keyword_n,
char const *name,
size_t name_n,
sm_conf_token_T *token)
{
sm_conf_node_T *n;
SM_IS_CONF(smc);
if ((n = sm_conf_node_new(smc, token)) != NULL)
{
n->scn_section.scns_type = SM_CONF_NODE_SECTION;
n->scn_section.scns_keyword = keyword;
n->scn_section.scns_keyword_n = keyword_n;
n->scn_section.scns_name = name;
n->scn_section.scns_name_n = name_n;
n->scn_section.scns_head = NULL;
n->scn_section.scns_tail = &n->scn_section.scns_head;
n->scn_section.scns_n = 0;
}
return n;
}
/*
** SM_CONF_SECTION_APPEND -- Add an option or subsection
** to an existing section.
**
** Parameters:
** smc -- context handle
** section -- existing section
** name -- name of the entry
** name_n -- # of bytes pointed to by <name>
** node -- value of the entry
**
** Returns:
** None
**
** Last code review:
** Last code change: 2004-05-20 21:16:56 jutta
*/
void
sm_conf_section_append(
sm_conf_T *smc,
sm_conf_node_T *section,
char const *name,
size_t name_n,
sm_conf_node_T *node)
{
SM_IS_CONF(smc);
SM_IS_CONF_NODE(section);
SM_IS_CONF_NODE(node);
node->scn_generic.scng_slot = name;
node->scn_generic.scng_slot_n = name_n;
*section->scn_section.scns_tail = node;
section->scn_section.scns_tail = &node->scn_generic.scng_next;
node->scn_generic.scng_parent = section;
section->scn_section.scns_n++;
}
/*
** SM_CONF_SECTION_NEXT -- get the next section element.
**
** Parameters:
** smc -- context handle
** section -- NULL or container node
** name -- out: name of the entry
** name_n -- out: # of bytes pointed to by <name>
** pred -- NULL or predecessor
**
** Returns:
** NULL once we're out of nodes or if the container
** isn't actually a section, otherwise
** the first node in the section (if <pred> is NULL)
** or <pred>'s successor.
**
** Last code review:
** Last code change:
*/
sm_conf_node_T *
sm_conf_section_next(
sm_conf_T *smc,
sm_conf_node_T const *section,
char const **name_out,
size_t *name_n_out,
sm_conf_node_T const *pred)
{
sm_conf_node_T *node;
if (name_out != NULL)
*name_out = NULL;
if (name_n_out != NULL)
*name_n_out = 0;
SM_IS_CONF(smc);
if (section != NULL)
SM_IS_CONF_NODE(section);
if (pred != NULL)
SM_IS_CONF_NODE(pred);
if (section == NULL || section->scn_type != SM_CONF_NODE_SECTION)
return NULL;
node = (pred == NULL
? section->scn_section.scns_head
: pred->scn_generic.scng_next);
if (node == NULL)
return NULL;
if (name_out != NULL)
*name_out = node->scn_generic.scng_slot;
if (name_n_out != NULL)
*name_n_out = node->scn_generic.scng_slot_n;
return node;
}
/*
** SM_CONF_SECTION_NEXT_SUBSECTION -- get the next subsection
**
** Parameters:
** smc -- context handle
** parent -- NULL or parent section
** keyword -- if non-NULL, must match this keyword.
** keyword_n -- # of bytes pointed to by keyword
** name -- if non-NULL, must match this name.
** name_n -- # of bytes pointed to by the name.
** pred -- NULL or predecessor
**
** Returns:
** NULL once we're out of nodes, otherwise
** the first node in the section (if <pred> is NULL)
** or <pred>'s successor.
**
** Last code review:
** Last code change: 2004-05-20 21:21:11 jutta
*/
sm_conf_node_T *
sm_conf_section_next_subsection(
sm_conf_T *smc,
sm_conf_node_T const *parent,
char const *keyword,
size_t keyword_n,
char const *name,
size_t name_n,
sm_conf_node_T const *pred)
{
SM_IS_CONF(smc);
if (parent != NULL)
SM_IS_CONF_NODE(parent);
if (pred != NULL)
SM_IS_CONF_NODE(pred);
while ( (pred = sm_conf_section_next(smc, parent, NULL, NULL, pred))
!= NULL)
{
if (pred->scn_type != SM_CONF_NODE_SECTION)
continue;
if ( keyword != NULL
&& !sm_memncaseeq(pred->scn_section.scns_keyword,
pred->scn_section.scns_keyword_n,
keyword, keyword_n))
continue;
if ( name != NULL
&& !sm_memncaseeq(pred->scn_section.scns_name,
pred->scn_section.scns_name_n,
name, name_n))
continue;
break;
}
return (sm_conf_node_T *)pred;
}
/*
** SM_CONF_SECTION_NEXT_OPTION -- get the next named section element
**
** Parameters:
** smc -- context handle
** parent -- NULL or node pointer of containing section
** name -- if non-NULL, must match this name.
** name_n -- # of bytes pointed to by the name.
** pred -- NULL or predecessor
**
** Returns:
** NULL once we're out of nodes, otherwise
** the first node in the section (if <pred> is NULL)
** or <pred>'s successor.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
sm_conf_node_T *
sm_conf_section_next_option(
sm_conf_T *smc,
sm_conf_node_T const *parent,
char const *name,
size_t name_n,
sm_conf_node_T const *pred)
{
char const *elem;
size_t elem_n;
SM_IS_CONF(smc);
if (parent != NULL)
SM_IS_CONF_NODE(parent);
if (pred != NULL)
SM_IS_CONF_NODE(pred);
while ( (pred = sm_conf_section_next(smc, parent, &elem, &elem_n, pred))
!= NULL)
{
if (elem == NULL || elem_n != name_n)
continue;
if ( name != NULL
&& !sm_memncaseeq(elem, elem_n, name, name_n))
continue;
break;
}
return (sm_conf_node_T *)pred;
}
/*
** SM_CONF_SECTION_KEYWORD -- get a section's keyword.
**
** The keyword is the first identifier associated with the section;
** e.g., in
** interface "local" { }
** the keyword is "interface".
**
** Parameters:
** smc -- context handle
** section -- NULL or a node pointer
** kw_out -- out: section keyword.
** kw_n_out -- out: # of bytes pointed to by <*kw_out>
**
** Returns:
** SM_CONF_ERR_INVALID if this isn't a section, otherwise 0.
**
** Last code review:
** Last code change: 2004-05-20 21:21:40
*/
int
sm_conf_section_keyword(
sm_conf_T *smc,
sm_conf_node_T const *section,
char const **kw_out,
size_t *kw_n_out)
{
SM_IS_CONF(smc);
if (section != NULL)
SM_IS_CONF_NODE(section);
if ( section == NULL
|| section->scn_type != SM_CONF_NODE_SECTION)
return SM_CONF_ERR_INVALID;
if (kw_out != NULL)
*kw_out = section->scn_section.scns_keyword;
if (kw_n_out != NULL)
*kw_n_out = section->scn_section.scns_keyword_n;
return 0;
}
/*
** SM_CONF_SECTION_NAME -- get a section's name.
**
** The name is the second identifier associated with the section;
** e.g., in
** interface "local" { }
** the name is "local".
**
** If the passed-in node is not a section, the call fails.
** If a section is anonymous, the function call succeeds, but
** the assigned pointer and length will be null and zero, respectively.
**
** Parameters:
** smc -- context handle
** section -- NULL or a node pointer
** name_out -- out: section name.
** name_n_out -- out: # of bytes pointed to by <*name_n_out>
**
** Returns:
** SM_CONF_ERR_INVALID if this isn't a section, otherwise 0.
**
** Last code review:
** Last code change: 2004-05-20 21:21:53
*/
int
sm_conf_section_name(
sm_conf_T *smc,
sm_conf_node_T const *section,
char const **name_out,
size_t *name_n_out)
{
SM_IS_CONF(smc);
if (section != NULL)
SM_IS_CONF_NODE(section);
if ( section == NULL
|| section->scn_type != SM_CONF_NODE_SECTION)
return SM_CONF_ERR_INVALID;
if (name_out != NULL)
*name_out = section->scn_section.scns_name;
if (name_n_out != NULL)
*name_n_out = section->scn_section.scns_name_n;
return 0;
}
/*
** SM_CONF_SECTION_N -- get # of entries of a section.
**
** Parameters:
** smc -- context handle
** section -- NULL or a node pointer
**
** Returns:
** 0 if this isn't a section, otherwise the number
** of entries.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
size_t
sm_conf_section_n(sm_conf_T *smc, sm_conf_node_T const *section)
{
SM_IS_CONF(smc);
if (section != NULL)
SM_IS_CONF_NODE(section);
if ( section == NULL
|| section->scn_type != SM_CONF_NODE_SECTION)
return 0;
return section->scn_section.scns_n;
}
/*
** SM_CONF_NODE_DESTROY -- free a node and its subnodes.
**
** Parameters:
** smc -- context handle
** node -- node to destroy.
**
** Returns:
** 0 on success, an error otherwise.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
void
sm_conf_node_destroy(sm_conf_T *smc, sm_conf_node_T *node)
{
sm_conf_node_T *n, *nn;
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
if (node == NULL)
return;
switch (node->scn_type)
{
case SM_CONF_NODE_LIST:
for (n = node->scn_list.scnl_head; n != NULL; n = nn)
{
nn = n->scn_generic.scng_next;
sm_conf_node_destroy(smc, n);
}
sm_conf_node_cache_free(node);
break;
case SM_CONF_NODE_VALUE:
sm_conf_node_cache_free(node);
break;
case SM_CONF_NODE_SECTION:
for (n = node->scn_section.scns_head; n != NULL; n = nn)
{
nn = n->scn_generic.scng_next;
sm_conf_node_destroy(smc, n);
}
break;
}
node->sm_magic = SM_MAGIC_NULL;
sm_free(node);
}
/*
** SM_CONF_ROOT -- get root of the configuration tree.
**
** Parameters:
** smc -- NULL or context handle
**
** Returns:
** NULL if smc is NULL or hasn't been successfully parsed.
** ((ca) hasn't been successfully parsed???)
** Otherwise, the root of smc's configuration context.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
sm_conf_node_T *
sm_conf_root(sm_conf_T *smc)
{
if (smc == NULL)
return NULL;
SM_IS_CONF(smc);
return smc->smc_root;
}
/*
** SM_CONF_NODE_TYPE -- get the type of a configuration node.
**
** Parameters:
** smc -- context handle
** node -- node whose type we're interested in.
**
** Returns:
** SM_CONF_NODE_NONE if the node is NULL.
** Otherwise, the type of the node:
** SM_CONF_NODE_VALUE for a single value
** SM_CONF_NODE_LIST for a list
** SM_CONF_NODE_SECTION for a section of named values.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
int
sm_conf_node_type(sm_conf_T *smc, sm_conf_node_T const *node)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
return node == NULL ? 0 : node->scn_type;
}
/*
** SM_CONF_NODE_TYPE_NAME -- get the type of a configuration node, as a string
**
** Parameters:
** smc -- context handle
** node -- node whose type we're interested in.
**
** Returns:
** "null" if the node is NULL.
** Otherwise, the type of the node:
** "value" for a single value
** "list" for a list
** "section" for a section of named values.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
char const *
sm_conf_node_type_name(sm_conf_T *smc, sm_conf_node_T const *node)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
if (node == NULL)
return "null";
switch (node->scn_type)
{
case SM_CONF_NODE_VALUE:
return "value";
case SM_CONF_NODE_LIST:
return "value";
case SM_CONF_NODE_SECTION:
return "value";
default:
break;
}
return "unexpected node type";
}
/*
** SM_CONF_NODE_NEXT -- get the next node, or NULL
**
** Parameters:
** smc -- context handle
** node -- node whose sibling we're interested in.
**
** Returns:
** NULL if the node is the root node;
** Otherwise, the node's parent.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
sm_conf_node_T *
sm_conf_node_next(sm_conf_T *smc, sm_conf_node_T const *node)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
return node == NULL ? NULL : node->scn_generic.scng_next;
}
/*
** SM_CONF_NODE_PARENT -- get the containing node, or NULL
**
** Parameters:
** smc -- context handle
** node -- node whose container we're interested in.
**
** Returns:
** NULL if the node is the root node;
** Otherwise, the node's parent.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
sm_conf_node_T *
sm_conf_node_parent(sm_conf_T *smc, sm_conf_node_T const *node)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
return node == NULL ? 0 : node->scn_generic.scng_parent;
}
/*
** SM_CONF_NODE_LOCATION -- print node location for an error message
**
** Parameters:
** smc -- context handle
** node -- node we're interested in.
** buf -- buffer for assembling result
** bufsize -- number of bytes pointed to by <buf>
**
** Returns:
** a rendered version of the source location of
** an error involving <node>.
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
char const *
sm_conf_node_location(
sm_conf_T *smc,
sm_conf_node_T const *node,
char *buf,
size_t bufsize)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
if (bufsize == 0)
return smc->smc_name ? smc->smc_name : "*stdin*";
SM_REQUIRE(buf != NULL);
if (node != NULL && node->scn_generic.scng_token.sct_line > 0)
snprintf(buf, bufsize, "%s:%d",
smc->smc_name ? smc->smc_name : "*stdin*",
node->scn_generic.scng_token.sct_line);
else
snprintf(buf, bufsize, "%s",
smc->smc_name ? smc->smc_name : "*stdin*");
return buf;
}
/*
** SM_CONF_NODE_TERMINATE -- terminate node values
**
** '\0'-terminate all strings in a parsed subtree.
**
** We can't do this on the fly because we may still be
** needing the characters right after strings in the
** text we're parsing (e.g., the commas and {} in {1,2,3}.
**
** But once everything is parsed, every string has at least
** one element of punctuation after it, and we can drop
** '\0' after each string.
**
** Parameters:
** smc -- context handle
** node -- subtree we're terminating
**
** Returns:
** none
**
** Last code review:
** Last code change: 2004-05-20 21:21:25 jutta
*/
void
sm_conf_node_terminate(sm_conf_T *smc, sm_conf_node_T *node)
{
SM_IS_CONF(smc);
if (node != NULL)
SM_IS_CONF_NODE(node);
if (node == NULL)
return;
switch (node->scn_type)
{
case SM_CONF_NODE_VALUE:
((char *)node->scn_value.scnv_token.sct_text)
[node->scn_value.scnv_token.sct_text_n] = '\0';
break;
case SM_CONF_NODE_LIST:
for (node = node->scn_list.scnl_head;
node != NULL;
node = node->scn_generic.scng_next)
sm_conf_node_terminate(smc, node);
break;
case SM_CONF_NODE_SECTION:
if (node->scn_section.scns_keyword != NULL)
((char *)node->scn_section.scns_keyword)
[node->scn_section.scns_keyword_n] = '\0';
if (node->scn_section.scns_name != NULL)
((char *)node->scn_section.scns_name)
[node->scn_section.scns_name_n] = '\0';
for (node = node->scn_section.scns_head;
node != NULL;
node = node->scn_generic.scng_next)
{
if (node->scn_generic.scng_slot != NULL)
((char *)node->scn_generic.scng_slot)
[node->scn_generic.scng_slot_n] = '\0';
sm_conf_node_terminate(smc, node);
}
break;
default:
return;
}
}
syntax highlighted by Code2HTML, v. 0.9.1