/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "zbxnum.h" #include "log.h" /****************************************************************************** * * * Purpose: check if the string is unsigned integer within the specified * * range and optionally store it into value parameter * * * * Parameters: str - [IN] string to check * * n - [IN] string length or ZBX_MAX_UINT64_LEN * * value - [OUT] a pointer to output buffer where the converted * * value is to be written (optional, can be NULL) * * size - [IN] size of the output buffer (optional) * * min - [IN] the minimum acceptable value * * max - [IN] the maximum acceptable value * * * * Return value: SUCCEED - the string is unsigned integer * * FAIL - the string is not a number or its value is outside * * the specified range * * * ******************************************************************************/ int zbx_is_uint_n_range(const char *str, size_t n, void *value, size_t size, zbx_uint64_t min, zbx_uint64_t max) { zbx_uint64_t value_uint64 = 0, c; const zbx_uint64_t max_uint64 = ~__UINT64_C(0); if ('\0' == *str || 0 == n || sizeof(zbx_uint64_t) < size || (0 == size && NULL != value)) return FAIL; while ('\0' != *str && 0 < n--) { if (0 == isdigit(*str)) return FAIL; /* not a digit */ c = (zbx_uint64_t)(unsigned char)(*str - '0'); if ((max_uint64 - c) / 10 < value_uint64) return FAIL; /* maximum value exceeded */ value_uint64 = value_uint64 * 10 + c; str++; } if (min > value_uint64 || value_uint64 > max) return FAIL; if (NULL != value) { /* On little endian architecture the output value will be stored starting from the first bytes */ /* of 'value' buffer while on big endian architecture it will be stored starting from the last */ /* bytes. We handle it by storing the offset in the most significant byte of short value and */ /* then use the first byte as source offset. */ unsigned short value_offset = (unsigned short)((sizeof(zbx_uint64_t) - size) << 8); memcpy(value, (unsigned char *)&value_uint64 + *((unsigned char *)&value_offset), size); } return SUCCEED; } /****************************************************************************** * * * Purpose: check if the string is unsigned hexadecimal integer within the * * specified range and optionally store it into value parameter * * * * Parameters: str - [IN] string to check * * n - [IN] string length * * value - [OUT] a pointer to output buffer where the converted * * value is to be written (optional, can be NULL) * * size - [IN] size of the output buffer (optional) * * min - [IN] the minimum acceptable value * * max - [IN] the maximum acceptable value * * * * Return value: SUCCEED - the string is unsigned integer * * FAIL - the string is not a hexadecimal number or its value * * is outside the specified range * * * ******************************************************************************/ int zbx_is_hex_n_range(const char *str, size_t n, void *value, size_t size, zbx_uint64_t min, zbx_uint64_t max) { zbx_uint64_t value_uint64 = 0, c; const zbx_uint64_t max_uint64 = ~__UINT64_C(0); int len = 0; if ('\0' == *str || 0 == n || sizeof(zbx_uint64_t) < size || (0 == size && NULL != value)) return FAIL; while ('\0' != *str && 0 < n--) { if ('0' <= *str && *str <= '9') c = *str - '0'; else if ('a' <= *str && *str <= 'f') c = 10 + (*str - 'a'); else if ('A' <= *str && *str <= 'F') c = 10 + (*str - 'A'); else return FAIL; /* not a hexadecimal digit */ if (16 < ++len && (max_uint64 >> 4) < value_uint64) return FAIL; /* maximum value exceeded */ value_uint64 = (value_uint64 << 4) + c; str++; } if (min > value_uint64 || value_uint64 > max) return FAIL; if (NULL != value) { /* On little endian architecture the output value will be stored starting from the first bytes */ /* of 'value' buffer while on big endian architecture it will be stored starting from the last */ /* bytes. We handle it by storing the offset in the most significant byte of short value and */ /* then use the first byte as source offset. */ unsigned short value_offset = (unsigned short)((sizeof(zbx_uint64_t) - size) << 8); memcpy(value, (unsigned char *)&value_uint64 + *((unsigned char *)&value_offset), size); } return SUCCEED; } static double ZBX_FLOAT_EPSILON = 0.0001; static double ZBX_DOUBLE_EPSILON = 2.22e-16; double zbx_get_float_epsilon(void) { return ZBX_FLOAT_EPSILON; } double zbx_get_double_epsilon(void) { return ZBX_DOUBLE_EPSILON; } void zbx_update_epsilon_to_float_precision(void) { ZBX_DOUBLE_EPSILON = 0.000001; } void zbx_update_epsilon_to_python_compatible_precision(void) { ZBX_DOUBLE_EPSILON = 0.0001; } int zbx_double_compare(double a, double b) { return fabs(a - b) <= ZBX_DOUBLE_EPSILON ? SUCCEED : FAIL; } int zbx_validate_value_dbl(double value, int dbl_precision) { if ((ZBX_DB_DBL_PRECISION_ENABLED == dbl_precision && (value < -1e+308 || value > 1e+308)) || (ZBX_DB_DBL_PRECISION_ENABLED != dbl_precision && (value <= -1e12 || value >= 1e12))) { return FAIL; } return SUCCEED; } /****************************************************************************** * * * Purpose: check if integer matches a list of integers * * * * Parameters: list - integers [i1-i2,i3,i4,i5-i6] (10-25,45,67-699) * * value - integer to check * * * * Return value: FAIL - out of period, SUCCEED - within the period * * * ******************************************************************************/ int zbx_int_in_list(char *list, int value) { char *start = NULL, *end = NULL, c = '\0'; int i1, i2, ret = FAIL; zabbix_log(LOG_LEVEL_DEBUG, "In %s() list:'%s' value:%d", __func__, list, value); for (start = list; '\0' != *start;) { if (NULL != (end = strchr(start, ','))) { c = *end; *end = '\0'; } if (2 == sscanf(start, "%d-%d", &i1, &i2)) { if (i1 <= value && value <= i2) { ret = SUCCEED; break; } } else { if (value == atoi(start)) { ret = SUCCEED; break; } } if (NULL != end) { *end = c; start = end + 1; } else break; } if (NULL != end) *end = c; zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); return ret; } static int is_double_valid_syntax(const char *str) { int len; /* Valid syntax is a decimal number optionally followed by a decimal exponent. */ /* Leading and trailing white space, NAN, INF and hexadecimal notation are not allowed. */ if ('-' == *str || '+' == *str) /* check leading sign */ str++; if (FAIL == zbx_number_parse(str, &len)) return FAIL; return '\0' == *(str + len) ? SUCCEED : FAIL; } /****************************************************************************** * * * Purpose: validate and optionally convert a string to a number of type * * 'double' * * * * Parameters: str - [IN] string to check * * value - [OUT] output buffer where to write the converted value * * (optional, can be NULL) * * * * Return value: SUCCEED - the string can be converted to 'double' and * * was converted if 'value' is not NULL * * FAIL - the string does not represent a valid 'double' or * * its value is outside of valid range * * * ******************************************************************************/ int zbx_is_double(const char *str, double *value) { double tmp; char *endptr; /* Not all strings accepted by strtod() can be accepted in Zabbix. */ /* Therefore additional, more strict syntax check is used before strtod(). */ if (SUCCEED != is_double_valid_syntax(str)) return FAIL; errno = 0; tmp = strtod(str, &endptr); if ('\0' != *endptr || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) return FAIL; if (NULL != value) *value = tmp; return SUCCEED; } #if defined(_WINDOWS) || defined(__MINGW32__) int zbx_wis_uint(const wchar_t *wide_string) { const wchar_t *wide_char = wide_string; if (L'\0' == *wide_char) return FAIL; while (L'\0' != *wide_char) { if (0 != iswdigit(*wide_char)) { wide_char++; continue; } return FAIL; } return SUCCEED; } #endif /****************************************************************************** * * * Purpose: converts double value to string and truncates insignificant * * precision * * * * Parameters: buffer - [OUT] the output buffer * * size - [IN] the output buffer size * * val - [IN] double value to be converted * * * * Return value: the output buffer with printed value * * * ******************************************************************************/ const char *zbx_print_double(char *buffer, size_t size, double val) { zbx_snprintf(buffer, size, "%.15G", val); if (atof(buffer) != val) zbx_snprintf(buffer, size, ZBX_FS_DBL64, val); return buffer; } /****************************************************************************** * * * Purpose: parse a number like "12.345" * * * * Parameters: number - [IN] start of number * * len - [OUT] length of parsed number * * * * Return value: SUCCEED - the number was parsed successfully * * FAIL - invalid number * * * * Comments: !!! Don't forget to sync the code with PHP !!! * * The token field locations are specified as offsets from the * * beginning of the expression. * * * ******************************************************************************/ int zbx_number_parse(const char *number, int *len) { int digits = 0, dots = 0; *len = 0; while (1) { if (0 != isdigit(number[*len])) { (*len)++; digits++; continue; } if ('.' == number[*len]) { (*len)++; dots++; continue; } if ('e' == number[*len] || 'E' == number[*len]) { (*len)++; if ('-' == number[*len] || '+' == number[*len]) (*len)++; if (0 == isdigit(number[*len])) return FAIL; while (0 != isdigit(number[++(*len)])); if ('.' == number[*len] ||'e' == number[*len] || 'E' == number[*len]) return FAIL; } if (1 > digits || 1 < dots) return FAIL; return SUCCEED; } } /****************************************************************************** * * * Purpose: convert string to 64bit unsigned integer * * * * Parameters: str - string to convert * * value - a pointer to converted value * * * * Return value: SUCCEED - the string is unsigned integer * * FAIL - otherwise * * * * Comments: the function automatically processes suffixes K, M, G, T * * * ******************************************************************************/ int zbx_str2uint64(const char *str, const char *suffixes, zbx_uint64_t *value) { size_t sz; const char *p; int ret; zbx_uint64_t factor = 1; sz = strlen(str); p = str + sz - 1; if (NULL != strchr(suffixes, *p)) { factor = suffix2factor(*p); sz--; } if (SUCCEED == (ret = zbx_is_uint64_n(str, sz, value))) *value *= factor; return ret; } /****************************************************************************** * * * Purpose: Removes spaces from both ends of the string, then unquotes it if * * double quotation mark is present on both ends of the string. If * * strip_plus_sign is non-zero, then removes single "+" sign from * * the beginning of the trimmed and unquoted string. * * * * This function does not guarantee that the resulting string * * contains numeric value. It is meant to be used for removing * * "valid" characters from the value that is expected to be numeric * * before checking if value is numeric. * * * * Parameters: str - [IN/OUT] string for processing * * strip_plus_sign - [IN] non-zero if "+" should be stripped * * * ******************************************************************************/ static void zbx_trim_number(char *str, int strip_plus_sign) { char *left = str; /* pointer to the first character */ char *right = strchr(str, '\0') - 1; /* pointer to the last character, not including terminating null-char */ if (left > right) { /* string is empty before any trimming */ return; } while (' ' == *left) { left++; } while (' ' == *right && left < right) { right--; } if ('"' == *left && '"' == *right && left < right) { left++; right--; } if (0 != strip_plus_sign && '+' == *left) { left++; } if (left > right) { /* string is empty after trimming */ *str = '\0'; return; } if (str < left) { while (left <= right) { *str++ = *left++; } *str = '\0'; } else { *(right + 1) = '\0'; } } /****************************************************************************** * * * Purpose: Removes spaces from both ends of the string, then unquotes it if * * double quotation mark is present on both ends of the string, then * * removes single "+" sign from the beginning of the trimmed and * * unquoted string. * * * * This function does not guarantee that the resulting string * * contains integer value. It is meant to be used for removing * * "valid" characters from the value that is expected to be numeric * * before checking if value is numeric. * * * * Parameters: str - [IN/OUT] string for processing * * * ******************************************************************************/ void zbx_trim_integer(char *str) { zbx_trim_number(str, 1); } /****************************************************************************** * * * Purpose: Removes spaces from both ends of the string, then unquotes it if * * double quotation mark is present on both ends of the string. * * * * This function does not guarantee that the resulting string * * contains floating-point number. It is meant to be used for * * removing "valid" characters from the value that is expected to be * * numeric before checking if value is numeric. * * * * Parameters: str - [IN/OUT] string for processing * * * ******************************************************************************/ void zbx_trim_float(char *str) { zbx_trim_number(str, 0); } /****************************************************************************** * * * Purpose: check if the string is a hexadecimal representation of data in * * the form "F4 CE 46 01 0C 44 8B F4\nA0 2C 29 74 5D 3F 13 49\n" * * * * Parameters: str - string to check * * * * Return value: SUCCEED - the string is formatted like the example above * * FAIL - otherwise * * * ******************************************************************************/ int zbx_is_hex_string(const char *str) { if ('\0' == *str) return FAIL; while ('\0' != *str) { if (0 == isxdigit(*str)) return FAIL; if (0 == isxdigit(*(str + 1))) return FAIL; if ('\0' == *(str + 2)) break; if (' ' != *(str + 2) && '\n' != *(str + 2)) return FAIL; str += 3; } return SUCCEED; } /****************************************************************************** * * * Purpose: validate and optionally convert a string to a number of type * * 'int' * * * * Parameters: str - [IN] string to check * * value - [OUT] output buffer where to write the converted value * * (optional, can be NULL) * * * * Return value: SUCCEED - the string can be converted to 'int' and * * was converted if 'value' is not NULL * * FAIL - the string does not represent a valid 'int' or * * its value is outside of valid range * * * ******************************************************************************/ int zbx_is_int(const char *str, int *value) { const char *ptr; zbx_uint32_t value_ui32; int sign; if ('-' == *(ptr = str)) { ptr++; sign = -1; } else sign = 1; if (SUCCEED != zbx_is_uint31(ptr, &value_ui32)) return FAIL; if (NULL != value) *value = ((int)value_ui32 * sign); return SUCCEED; }