/* * 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 #include #include #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 };