/* * 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-u32.c,v 1.4 2004/09/02 01:14:31 ca Exp $") #if SM_LIBCONF_ALONE #include #include #include "sm-conf.h" #else /* SM_LIBCONF_ALONE */ #include "sm/ctype.h" #include "sm/error.h" #include "sm/sm-conf.h" #endif /* SM_LIBCONF_ALONE */ #include "sm-conf-u32.h" #include "sm-conf-util.h" #include "sm-conf-state.h" /* ** SM_MEMTOU32 -- strtoul without the nullbyte and the sign, sort of. ** ** Convert at most bytes pointed to by into ** an unsigned long no larger than 2^32-1. ** ** Assign a pointer to the first unconverted byte to ** *endptr. ** ** Parameters: ** text -- the text to be converted ** text_n -- # of bytes pointed to by text ** endptr -- if non-NULL, a pointer to the first ** unconverted byte is stored here. ** base -- 0 for dynamic, otherwise the base ** of the number. For bases above 10, ** an optional leading 0x is skipped. ** val_out -- value is stored here. ** ** Returns: ** EINVAL for a format error ** ERANGE for overflow ** 0 for success. */ int sm_memtou32( char const *text, size_t text_n, char **endptr, int base, unsigned long *val_out) { unsigned long val, n; size_t i; if (endptr != NULL) *endptr = NULL; if (val_out != NULL) *val_out = 0; i = 0; n = 0; /* Skip leading spaces. */ while (i < text_n && ISSPACE(text[i])) i++; if (text_n > i && text[i] == '0') { if ( text_n > 1 && (base > 10 || base == 0) && (text[i + 1] == 'x' || text[i + 1] == 'X')) { if (base == 0) base = 16; i += 2; } else { if (base == 0) base = 8; } } else { if (base == 0) base = 10; } if ( i >= text_n || !isascii(text[i]) || !(base > 10 ? isalnum(text[i]) : isdigit(text[i]))) return EINVAL; for (; i < text_n; i++) { if (!isascii(text[i]) || !isalnum(text[i])) break; if (isdigit(text[i])) { if ((val = text[i] - '0') >= base) return EINVAL; } else if (base > 10) { char const *ptr; char const *abc = "abcdefghijklmnopqrstuvwxyz"; ptr = strchr(abc, tolower(text[i])); if (ptr == NULL) break; if (10 + (ptr - abc) >= base) break; val = 10 + (ptr - abc); } else break; if ((UINT32_MAX - val) / base < n) return ERANGE; n = n * base + val; } if (endptr != NULL) *endptr = (char *)(text + i); if (val_out != NULL) *val_out = n; return 0; } /* ** SM_CONF_U32_SCAN -- scan an integer into an ulong, possibly with suffixes. ** ** Civilized version of sm_memtou32. Prints error messages ** and evaluates suffixes under control of a definition. ** ** Parameters: ** smc -- context ** node -- NULL or node, for error messages ** def -- definition of the value; def->scd_contents are used ** for suffix values ** text -- text to scan ** text_n -- # of bytes we can access ** val_out -- result is stored here ** ** Returns: ** 0 on sucecss, an error code on error. ** ** Note: should it be optional to have additive suffixes? */ int sm_conf_u32_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) { char loc[SM_CONF_ERROR_BUFFER_SIZE]; size_t i, offset; int err; unsigned long val; char *endptr; SM_IS_CONF_DEF(def); SM_REQUIRE(val_out != NULL); offset = 0; val = 0; *val_out = 0; do { SM_ASSERT(offset <= text_n); err = sm_memtou32(text + offset, text_n - offset, &endptr, 0, &val); if (err != 0) { if (err == ERANGE) sm_conf_error_add(smc, "%s: overflow error in %s '%.*s'", sm_conf_node_location(smc, node, loc, sizeof loc), def->scd_name[0] == '\0' ? "value" : def->scd_name, (int)text_n, text); else sm_conf_error_add(smc, "%s: expected number, got '%.*s'", sm_conf_node_location(smc, node, loc, sizeof loc), (int)text_n, text); return SM_CONF_ERR_TYPE; } i = endptr - text; /* skip over leading white space */ while (i < text_n && ISSPACE(text[i])) i++; /* If there's something left, and we have suffixes, use them. */ if (i < text_n) { sm_conf_definition_T const * suffix; size_t len; suffix = sm_conf_subdef_prefix( def->scd_contents, sm_conf_type_u32_suffix, text + i, text_n - i, &len); SM_IS_CONF_DEF(def); if (suffix == NULL || suffix->scd_offset == 0) { sm_conf_error_add(smc, "%s: unexpected trailing characters in" " '%.*s'", sm_conf_node_location(smc, node, loc, sizeof loc), (int)text_n, text); return SM_CONF_ERR_TYPE; } if ((UINT32_MAX / suffix->scd_offset) < val) { sm_conf_error_add(smc, "%s: overflow error in %s '%.*s'", sm_conf_node_location(smc, node, loc, sizeof loc), def->scd_name[0] == '\0' ? "value" : def->scd_name, (int)text_n, text); return SM_CONF_ERR_TYPE; } val = val * suffix->scd_offset; i += len; } *val_out += val; offset = i; } while (i < text_n); return 0; } /* ** SM_CONF_U32_STORE -- store an unsigned long at a location ** ** Parameters: ** data -- the location to store at ** size -- # of bytes pointed to by ** val -- the value to store ** ** Returns: ** 0 if the size is one of the supported integer ** sizes, otherwise SM_CONF_ERR_INVALID */ int sm_conf_u32_store( void *data, size_t size, unsigned long val) { if (size == 1) *(unsigned char *)data = val; else if (size == sizeof(unsigned short)) *(unsigned short *)data = val; else if (size == sizeof(unsigned int)) *(unsigned int *)data = val; else if (size == sizeof(unsigned long)) *(unsigned long *)data = val; else return SM_CONF_ERR_INVALID; return 0; } /* ** SM_CONF_U32_LOAD -- load an unsigned long from a location ** ** Parameters: ** data -- the location to load from ** size -- # of bytes pointed to by ** val -- the loaded value is assigned to this address ** ** Returns: ** 0 if the size is one of the supported integer ** sizes, otherwise SM_CONF_ERR_INVALID */ int sm_conf_u32_load( void const *data, size_t size, unsigned long *val) { if (size == 1) *val = *(unsigned char const *)data; else if (size == sizeof(unsigned short)) *val = *(unsigned short const *)data; else if (size == sizeof(unsigned int)) *val = *(unsigned int const *)data; else if (size == sizeof(unsigned long)) *val = *(unsigned long const *)data; else return SM_CONF_ERR_INVALID; return 0; }