/*
* 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-type-choice.c,v 1.19 2006/04/10 18:11:45 ca Exp $")
#if SM_LIBCONF_ALONE
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include "sm-conf.h"
#include "sm-conf-util.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/limits.h"
#include "sm/ctype.h"
#include "sm/string.h"
#include "sm/memops.h"
#include "sm/sm-conf.h"
#endif /* SM_LIBCONF_ALONE */
#include "sm-conf-node.h"
#include "sm-conf-state.h"
#include "sm-conf-type.h"
#include "sm-conf-u32.h"
#include "sm-conf-util.h"
/* tag for single choice element */
sm_conf_type_T const
sm_conf_type_choice_value_data;
static int
choice_scan(
sm_conf_T *smc,
sm_conf_node_T const *node,
sm_conf_definition_T const *def,
char const *text,
size_t text_n,
unsigned long *val_out,
int *pnegate)
{
char loc[SM_CONF_ERROR_BUFFER_SIZE];
sm_conf_definition_T const *val;
int negate;
SM_IS_CONF_DEF(def);
if (text_n > 5 && strncasecmp(text, "dont_", 5) == 0)
negate = 5;
else if (text_n > 4 && strncasecmp(text, "not_", 4) == 0)
negate = 4;
else if (text_n > 3 && strncasecmp(text, "no_", 3) == 0)
negate = 3;
else if (text_n > 1 && (text[0] == '-' || text[0] == '~' ||
text[0] == '!'))
negate = 1;
else
negate = 0;
val = sm_conf_subdef(def->scd_contents,
sm_conf_type_choice_value,
text + negate, text_n - negate);
if (val == NULL)
{
if (def->scd_flags & SM_CONF_FLAG_NUMERIC)
{
unsigned long num;
/* allow a choice to be specified as a number. */
if (sm_conf_u32_scan(smc, NULL, def, text, text_n,
&num) == 0)
{
*val_out = num;
return 0;
}
}
sm_conf_error_add(smc, "%s: unexpected %s '%.*s'",
sm_conf_node_location(smc, node, loc, sizeof loc),
def->scd_flags & SM_CONF_FLAG_MULTIPLE
? "flag" : "choice",
(int)text_n, text);
return SM_CONF_ERR_TYPE;
}
if (pnegate != NULL)
*pnegate = negate;
*val_out = val->scd_offset;
return 0;
}
static int
choice_node_to_value_single(
sm_conf_T *smc,
sm_conf_definition_T const *def,
sm_conf_node_T *node,
void *data,
int *pnegate)
{
char const *text;
size_t text_n;
int err;
unsigned long val;
SM_IS_CONF_DEF(def);
err = sm_conf_node_to_value(smc, "choice", node, &text, &text_n);
if (err != 0)
return err;
err = choice_scan(smc, node, def, text, text_n, &val, pnegate);
if (err != 0)
return err;
if (data != NULL)
err = sm_conf_u32_store(data, def->scd_size, val);
return err;
}
static int
sm_conf_type_choice_node_to_value(
sm_conf_T *smc,
sm_conf_definition_T const *def,
sm_conf_node_T *node,
void *data)
{
int err, neg;
unsigned long val, ul;
SM_IS_CONF_DEF(def);
if (!(def->scd_flags & (SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_OR)))
return choice_node_to_value_single(smc, def, node, data, NULL);
val = 0;
err = 0;
if (data != NULL)
err = sm_conf_u32_load(data, def->scd_size, &val);
if (sm_conf_node_type(smc, node) == SM_CONF_NODE_LIST &&
(def->scd_flags & SM_CONF_FLAG_MULTIPLE))
{
sm_conf_node_T *ch = NULL;
while ((ch = sm_conf_list_next(smc, node, ch)) != NULL)
{
err = choice_node_to_value_single( smc, def, ch, data,
&neg);
if (err != 0)
return err;
if (data != NULL)
{
err = sm_conf_u32_load(data, def->scd_size,
&ul);
if (err != 0)
return err;
if (neg)
val &= ~ul;
else
val |= ul;
}
}
}
else
{
err = choice_node_to_value_single(smc, def, node, data, &neg);
if (err != 0)
return err;
if (data != NULL)
{
err = sm_conf_u32_load(data, def->scd_size, &ul);
if (err != 0)
return err;
if (neg)
val &= ~ul;
else
val |= ul;
}
}
if (data != NULL)
err = sm_conf_u32_store(data, def->scd_size, val);
return err;
}
static int
sm_conf_type_choice_value_check(
sm_conf_T *smc,
sm_conf_definition_T const *def,
void const *data)
{
SM_IS_CONF_DEF(def);
/* if we have a check function, use it. */
if (def->scd_check != NULL)
return (* def->scd_check)(smc, def->scd_check_data, def, data);
return 0;
}
static int
sm_conf_type_choice_value_null(
sm_conf_T *smc,
sm_conf_definition_T const *def,
void *data)
{
unsigned long val;
int err;
if (data != NULL)
{
val = 0;
if (def->scd_default != NULL)
{
err = choice_scan(smc, NULL, def, def->scd_default,
strlen(def->scd_default), &val, NULL);
#if 0
/*
** How to allow for multiple choices, e.g.,
** "{flag1, flags2}" as initial value?
** It would be necessary to scan and parse the
** string that contains the default value first.
*/
err = sm_conf_type_choice_node_to_value(smc, def,
def->scd_default,
strlen(def->scd_default), &val);
#endif
if (err != 0)
return err;
}
err = sm_conf_u32_store(data, def->scd_size, val);
if (err != 0)
return err;
}
return 0;
}
sm_conf_type_T const
sm_conf_type_choice_data =
{
sm_conf_type_choice_node_to_value,
sm_conf_type_choice_value_check,
sm_conf_type_choice_value_null
};
syntax highlighted by Code2HTML, v. 0.9.1