/*
* Copyright (c) 2004, 2006 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-get.c,v 1.16 2006/01/09 19:06:25 ca Exp $")
#if SM_LIBCONF_ALONE
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include "sm-util.h"
#include "sm-conf-util.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/ctype.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/string.h"
#endif /* SM_LIBCONF_ALONE */
#include "sm-conf-state.h"
#include "sm-conf-node.h"
#include "sm-conf-scan.h"
#include "sm-conf-token.h"
#include "sm-conf-type.h"
/*
** PATHNAME_TOKEN -- utility: extract token or token pair from a node name
**
** pathname: [segment [sep segment]]
** segment: name [open sub close]
** sep: +sepchar
** sepchar: <anything that cannot start an identifier>
** open: "<" | "(" | "{" | "[" | <"> | "'" | "`"
** close: ">" | ")" | "}" | "]" | <"> | "'"
**
** Opening and closing paren must match, respectively.
** A closing ' closes both an open ` (m4-style) and an open ' (sh-style).
**
** If we were to further restrict the separators (".", maybe?) and
** the open/close syntax, I'd be happy to make the parser code
** less ambiguous. This is simply the most generic syntax that
** lets us try different "looks". --Jutta
**
** Parameters:
** s -- (pointer to) string to scan
** e -- end of string
** name_out -- name (output)
** name_n_out -- length of name_out (output)
** sub_out -- sub (output)
** sub_n_out -- length of sub_out (output)
**
** Returns:
** true/false: did we get a token?
*/
static bool
pathname_token(
char const **s,
char const *e,
char const **name_out,
size_t *name_n_out,
char const **sub_out,
size_t *sub_n_out)
{
char const *p, *q;
char cl;
static char const obraces[] = "<({[\"'`",
cbraces[] = ">)}]\"''";
SM_REQUIRE(name_out != NULL);
SM_REQUIRE(name_n_out != NULL);
SM_REQUIRE(sub_out != NULL);
SM_REQUIRE(sub_n_out != NULL);
*name_out = NULL;
*name_n_out = 0;
*sub_out = NULL;
*sub_n_out = 0;
/* skip leading garbage */
for (p = *s; p < e && !isalnum(*p) && *p != '_'; p++)
;
/* skip an identifier */
*name_out = p;
for (; p < e && !(*p & 0x80) && (isalnum(*p) || *p == '_'); p++)
;
if (p == *name_out)
return false;
*name_n_out = p - *name_out;
/*
** If we've got some sort of bracketing action, this names both a
** section type and its title. Assign the title to the "sub"
** output parameter.
*/
if (*p != '\0' && (q = strchr(obraces, *p)) != NULL)
{
p++;
*sub_out = p;
cl = cbraces[q - obraces];
for (; p < e && *p != cl; p++)
{
if (*p == '\\')
{
p++;
/* backslash as last char? -> error! */
if (p >= e)
return 0;
}
}
*sub_n_out = p - *sub_out;
}
/* skip trailing garbage */
for (; p < e && !isalnum(*p) && *p != '_'; p++)
;
*s = p;
return true;
}
/*
** SM_CONF_GET_NODE_SCAN -- scan a type
**
** Parameters:
** smc -- handle
** parent -- NULL or node relative to which we're searching
** name -- path name of the option or section
** type -- type of the result value
** type_data -- parameters of the type.
** flags -- SM_CONF_FLAG_*, controls conversion process.
** data -- assign the result to the pointed-to address.
** size -- # of bytes pointed to by <data>.
**
** Returns:
** 0 on success,
**
** SM_CONF_ERR_NOT_FOUND if no value is available,
** other errors as returned by the type.
*/
static int
sm_conf_get_node_scan(
sm_conf_T *smc,
sm_conf_node_T *parent,
char const *name,
sm_conf_type_T const *type,
void const *type_data,
unsigned int flags,
void *data,
size_t size)
{
sm_conf_definition_T def[2];
def[0].sm_magic = SM_CONF_DEF_MAGIC;
def[0].scd_name = name;
def[0].scd_type = type;
def[0].scd_offset = 0;
def[0].scd_size = size;
def[0].scd_flags = flags;
def[0].scd_contents = type_data;
def[0].scd_default = NULL;
def[0].scd_check = NULL;
def[0].scd_check_data = NULL;
def[1].scd_name = NULL;
return sm_conf_scan_node(smc, def, parent, data);
}
/*
** SM_CONF_GET_RELATIVE -- get a property relative to a node.
**
** Parameters:
** smc -- handle
** node -- NULL or node relative to which we're searching
** name -- path name of the option or section
** type -- type of the result value
** type_data -- ancillary data for result value.
** flags -- SM_CONF_FLAG_* flags, or'ed together
** data -- assign the result to the pointed-to address.
** size -- # of bytes pointed to by <data>.
**
** Returns:
** 0 on success,
** SM_CONF_ERR_NOT_FOUND if no value is available,
** other errors as returned by the type.
*/
int
sm_conf_get_relative(
sm_conf_T *smc,
sm_conf_node_T *node,
char const *path,
sm_conf_type_T const *type,
void const *type_data,
unsigned int flags,
void *data,
size_t size)
{
char const *kw, *name, *elem;
size_t kw_n, name_n, elem_n;
int err;
sm_conf_node_T *sub;
if (path == NULL)
return sm_conf_get_node_scan(smc, node, "", type,
type_data, flags, data, size);
if (!pathname_token(&path, path + strlen(path),
&kw, &kw_n, &name, &name_n))
return sm_conf_get_node_scan(smc, node, "", type,
type_data, flags, data, size);
if (node == NULL)
return SM_CONF_ERR_NOT_FOUND;
if ( type != sm_conf_type_section
&& *path == '\0'
&& name == NULL)
{
/* We may be looking for a property. */
sub = NULL;
while ((sub = sm_conf_section_next(smc,
node, &elem, &elem_n, sub)) != NULL)
{
if (sm_memncaseeq(elem, elem_n, kw, kw_n))
{
return sm_conf_get_node_scan(smc, sub,
elem, type, type_data, flags,
data, size);
}
}
}
/* We are looking only for a subsection. */
sub = NULL;
while ((sub = sm_conf_section_next_subsection(smc, node,
kw, kw_n, NULL, 0, sub)) != NULL)
{
if (sm_conf_section_name(smc, sub, &elem, &elem_n))
continue;
if ( name == NULL
? elem == NULL
: sm_conf_token_match(
smc, name, name_n, elem, elem_n))
{
err = sm_conf_get_relative(smc, sub, path,
type, type_data, flags, data, size);
if (err == 0)
return err;
}
}
return SM_CONF_ERR_NOT_FOUND;
}
/*
** SM_CONF_GET -- get the value of a configuration option.
**
** Parameters:
** smc -- handle
** name -- path name of the option or section
** type -- type of the result value
** type_data -- ancillary data for result value.
** flags -- SM_CONF_FLAG_* flags, or'ed together
** data -- assign the result to the pointed-to address.
** size -- # of bytes pointed to by <data>.
**
** Returns:
** 0 on success,
** SM_CONF_ERR_NOT_FOUND if no value is available,
** other errors as returned by the type.
*/
int
sm_conf_get(
sm_conf_T *smc,
char const *name,
sm_conf_type_T const *type,
void const *type_data,
unsigned int flags,
void *data,
size_t size)
{
if (smc == NULL)
return SM_CONF_ERR_INVALID;
return sm_conf_get_relative(
smc, smc->smc_root, name, type, type_data, flags, data, size);
}
/*
** SM_CONF_SCAN_NEXT_RELATIVE -- get a property relative to a parent node.
**
** Parameters:
** smc -- handle
** parent -- a node that <path> is relative to, or NULL
** path -- pathname of the section we're searching for
** defs -- definition of the section elements
** flags -- SM_CONF_FLAG_* flags for the section
** name_out -- NULL or a place for a pointer to the section's name
** name_n_out -- NULL or a place for the section's name's length
** data -- assign the scanned section data to the pointed-to
** address.
** iter -- iteration pointer. Must point to NULL initially;
** otherwise opaque to the application.
**
** Returns:
** 0 on success,
** SM_CONF_ERR_NOT_FOUND if no value is available,
** other errors as returned by the type.
*/
int
sm_conf_scan_next_relative(
sm_conf_T *smc,
sm_conf_node_T *parent,
char const *path,
sm_conf_definition_T const *defs,
unsigned int flags,
char const **name_out,
size_t *name_n_out,
void *data,
void **iter)
{
int err;
sm_conf_node_T *prev, *my_prev;
sm_conf_definition_T section_def[2];
char const *name, *elem, *kw;
size_t name_n, elem_n, kw_n;
if (smc == NULL)
return SM_CONF_ERR_INVALID;
if (defs == NULL || defs->scd_name == NULL)
return SM_CONF_ERR_INVALID;
SM_IS_CONF_DEF(defs);
if (name_out != NULL)
*name_out = NULL;
if (name_n_out != NULL)
*name_n_out = 0;
if ( parent == NULL
&& (parent = smc->smc_root) == NULL)
return SM_CONF_ERR_NOT_FOUND;
if (path == NULL)
path = "";
prev = (iter == NULL ? NULL : *iter);
/*
** If we run out of <path> here, <parent> is the node we're
** looking for; assign it.
*/
if (!pathname_token(&path, path + strlen(path),
&kw, &kw_n, &name, &name_n))
{
if (prev != NULL)
return SM_CONF_ERR_NOT_FOUND;
/*
** If our definitions have a non-empty name, they're
** the contents of a section that we're looking for.
** Make the obvious definition for that section.
*/
if (defs->scd_name[0] != '\0')
{
sm_memset(§ion_def, 0, sizeof section_def);
section_def[0].sm_magic = SM_CONF_DEF_MAGIC;
section_def[0].scd_name = "";
section_def[0].scd_type = sm_conf_type_section;
section_def[0].scd_default = NULL;
section_def[0].scd_flags = flags;
section_def[0].scd_contents = defs;
section_def[1].scd_name = NULL;
defs = section_def;
}
err = sm_conf_scan_node(smc, defs, parent, data);
if (err == 0)
{
if (sm_conf_section_name(smc, parent,
&name, &name_n) == 0)
{
if (name_out != NULL)
*name_out = name;
if (name_n_out != NULL)
*name_n_out = name_n;
}
if (iter != NULL)
*iter = parent;
}
return err;
}
/*
** my_prev := the node directly below <parent> that's above
** or equal to <prev>
*/
my_prev = prev;
while (my_prev != NULL)
{
sm_conf_node_T *par;
par = sm_conf_node_parent(smc, my_prev);
if (par == parent)
break;
my_prev = par;
}
/* The `prev' pointer must be either NULL or below <parent>. */
if (prev != NULL && my_prev == NULL)
return SM_CONF_ERR_INVALID;
/*
** If we have a <my_prev>, and we can iterate successfully below
** that, use the result of that iteration.
*/
if (my_prev != NULL && my_prev != prev)
{
err = sm_conf_scan_next_relative(
smc, my_prev, path, defs, flags,
name_out, name_n_out, data, iter);
if (err != SM_CONF_ERR_NOT_FOUND)
return err;
}
/*
** We need to step outside the subtree controlled
** by <my_prev>.
*/
*iter = NULL;
while ((my_prev = sm_conf_section_next_subsection(smc,
parent, kw, kw_n, NULL, 0, my_prev)) != NULL)
{
/* The kw matches. How about the name? */
if (sm_conf_section_name(smc, my_prev,
&elem, &elem_n) != 0)
continue;
if ( name == NULL
|| ( elem != NULL
&& sm_conf_token_match(smc,
name, name_n, elem, elem_n)))
{
err = sm_conf_scan_next_relative(
smc, my_prev, path, defs,
flags, name_out, name_n_out,
data, iter);
if (err != SM_CONF_ERR_NOT_FOUND)
return err;
}
}
return SM_CONF_ERR_NOT_FOUND;
}
/*
** SM_CONF_SCAN_NEXT -- scan a named section.
**
** Successive calls to sm_conf_scan_next() return scanned
** successive instances of sections that match a pathname.
**
** Parameters:
** smc -- handle
** path -- pathname of the section we're searching for
** defs -- array of definitions of the section elements,
** terminated by a definition with NULL name.
** flags -- SM_CONF_FLAG_* flags for the section
** name_out -- NULL or a place for a pointer to the section's name
** name_n_out -- NULL or a place for the section's name's length
** data -- assign the scanned section data to the pointed-to
** address.
** iter -- iteration pointer. Must points to NULL initially;
** otherwise opaque to the application.
**
** Returns:
** 0 on success,
** SM_CONF_ERR_NOT_FOUND if no value is available,
** other errors as returned by the type.
*/
int
sm_conf_scan_next(
sm_conf_T *smc,
char const *path,
sm_conf_definition_T const *defs,
unsigned int flags,
char const **name_out,
size_t *name_n_out,
void *data,
void **iter)
{
return sm_conf_scan_next_relative(
smc, NULL, path, defs, flags, name_out, name_n_out, data, iter);
}
syntax highlighted by Code2HTML, v. 0.9.1