blob: 3232ea478bdfebb37fdf3a5d5e46cc679faa2361 [file] [log] [blame]
/*
This file is part of libmicrohttpd
Copyright (C) 2015, 2016 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file microhttpd/mhd_str.c
* @brief Functions implementations for string manipulating
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_str.h"
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
#include "mhd_limits.h"
#ifdef MHD_FAVOR_SMALL_CODE
#ifdef _MHD_static_inline
#undef _MHD_static_inline
#endif /* _MHD_static_inline */
/* Do not force inlining and do not use macro functions, use normal static
functions instead.
This may give more flexibility for size optimizations. */
#define _MHD_static_inline static
#ifndef INLINE_FUNC
#define INLINE_FUNC 1
#endif /* !INLINE_FUNC */
#endif /* MHD_FAVOR_SMALL_CODE */
/*
* Block of functions/macros that use US-ASCII charset as required by HTTP
* standards. Not affected by current locale settings.
*/
#ifdef INLINE_FUNC
#if 0 /* Disable unused functions. */
/**
* Check whether character is lower case letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is lower case letter, zero otherwise
*/
_MHD_static_inline bool
isasciilower (char c)
{
return (c >= 'a') && (c <= 'z');
}
#endif /* Disable unused functions. */
/**
* Check whether character is upper case letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is upper case letter, zero otherwise
*/
_MHD_static_inline bool
isasciiupper (char c)
{
return (c >= 'A') && (c <= 'Z');
}
#if 0 /* Disable unused functions. */
/**
* Check whether character is letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is letter in US-ASCII, zero otherwise
*/
_MHD_static_inline bool
isasciialpha (char c)
{
return isasciilower (c) || isasciiupper (c);
}
#endif /* Disable unused functions. */
/**
* Check whether character is decimal digit in US-ASCII
*
* @param c character to check
* @return non-zero if character is decimal digit, zero otherwise
*/
_MHD_static_inline bool
isasciidigit (char c)
{
return (c >= '0') && (c <= '9');
}
#if 0 /* Disable unused functions. */
/**
* Check whether character is hexadecimal digit in US-ASCII
*
* @param c character to check
* @return non-zero if character is decimal digit, zero otherwise
*/
_MHD_static_inline bool
isasciixdigit (char c)
{
return isasciidigit (c) ||
( (c >= 'A') && (c <= 'F') ) ||
( (c >= 'a') && (c <= 'f') );
}
/**
* Check whether character is decimal digit or letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is decimal digit or letter, zero otherwise
*/
_MHD_static_inline bool
isasciialnum (char c)
{
return isasciialpha (c) || isasciidigit (c);
}
#endif /* Disable unused functions. */
/**
* Convert US-ASCII character to lower case.
* If character is upper case letter in US-ASCII than it's converted to lower
* case analog. If character is NOT upper case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to lower case character
*/
_MHD_static_inline char
toasciilower (char c)
{
return isasciiupper (c) ? (c - 'A' + 'a') : c;
}
#if 0 /* Disable unused functions. */
/**
* Convert US-ASCII character to upper case.
* If character is lower case letter in US-ASCII than it's converted to upper
* case analog. If character is NOT lower case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to upper case character
*/
_MHD_static_inline char
toasciiupper (char c)
{
return isasciilower (c) ? (c - 'a' + 'A') : c;
}
#endif /* Disable unused functions. */
#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in MHD_str_to_uvalue_n_() */
/**
* Convert US-ASCII decimal digit to its value.
*
* @param c character to convert
* @return value of decimal digit or -1 if @ c is not decimal digit
*/
_MHD_static_inline int
todigitvalue (char c)
{
if (isasciidigit (c))
return (unsigned char) (c - '0');
return -1;
}
#endif /* MHD_FAVOR_SMALL_CODE */
/**
* Convert US-ASCII hexadecimal digit to its value.
*
* @param c character to convert
* @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
*/
_MHD_static_inline int
toxdigitvalue (char c)
{
if (isasciidigit (c))
return (unsigned char) (c - '0');
if ( (c >= 'A') && (c <= 'F') )
return (unsigned char) (c - 'A' + 10);
if ( (c >= 'a') && (c <= 'f') )
return (unsigned char) (c - 'a' + 10);
return -1;
}
#else /* !INLINE_FUNC */
/**
* Checks whether character is lower case letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is lower case letter,
* boolean false otherwise
*/
#define isasciilower(c) (((char) (c)) >= 'a' && ((char) (c)) <= 'z')
/**
* Checks whether character is upper case letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is upper case letter,
* boolean false otherwise
*/
#define isasciiupper(c) (((char) (c)) >= 'A' && ((char) (c)) <= 'Z')
/**
* Checks whether character is letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is letter, boolean false
* otherwise
*/
#define isasciialpha(c) (isasciilower (c) || isasciiupper (c))
/**
* Check whether character is decimal digit in US-ASCII
*
* @param c character to check
* @return boolean true if character is decimal digit, boolean false
* otherwise
*/
#define isasciidigit(c) (((char) (c)) >= '0' && ((char) (c)) <= '9')
/**
* Check whether character is hexadecimal digit in US-ASCII
*
* @param c character to check
* @return boolean true if character is hexadecimal digit,
* boolean false otherwise
*/
#define isasciixdigit(c) (isasciidigit ((c)) || \
(((char) (c)) >= 'A' && ((char) (c)) <= 'F') || \
(((char) (c)) >= 'a' && ((char) (c)) <= 'f') )
/**
* Check whether character is decimal digit or letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is decimal digit or letter,
* boolean false otherwise
*/
#define isasciialnum(c) (isasciialpha (c) || isasciidigit (c))
/**
* Convert US-ASCII character to lower case.
* If character is upper case letter in US-ASCII than it's converted to lower
* case analog. If character is NOT upper case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to lower case character
*/
#define toasciilower(c) ((isasciiupper (c)) ? (((char) (c)) - 'A' + 'a') : \
((char) (c)))
/**
* Convert US-ASCII character to upper case.
* If character is lower case letter in US-ASCII than it's converted to upper
* case analog. If character is NOT lower case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to upper case character
*/
#define toasciiupper(c) ((isasciilower (c)) ? (((char) (c)) - 'a' + 'A') : \
((char) (c)))
/**
* Convert US-ASCII decimal digit to its value.
*
* @param c character to convert
* @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
*/
#define todigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \
(int) (-1))
/**
* Convert US-ASCII hexadecimal digit to its value.
* @param c character to convert
* @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
*/
#define toxdigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \
( (((char) (c)) >= 'A' && ((char) (c)) <= 'F') ? \
(int) (((unsigned char) (c)) - 'A' + 10) : \
( (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ? \
(int) (((unsigned char) (c)) - 'a' + 10) : \
(int) (-1) )))
#endif /* !INLINE_FUNC */
#ifndef MHD_FAVOR_SMALL_CODE
/**
* Check two string for equality, ignoring case of US-ASCII letters.
*
* @param str1 first string to compare
* @param str2 second string to compare
* @return non-zero if two strings are equal, zero otherwise.
*/
int
MHD_str_equal_caseless_ (const char *str1,
const char *str2)
{
while (0 != (*str1))
{
const char c1 = *str1;
const char c2 = *str2;
if ( (c1 != c2) &&
(toasciilower (c1) != toasciilower (c2)) )
return 0;
str1++;
str2++;
}
return 0 == (*str2);
}
#endif /* ! MHD_FAVOR_SMALL_CODE */
/**
* Check two string for equality, ignoring case of US-ASCII letters and
* checking not more than @a maxlen characters.
* Compares up to first terminating null character, but not more than
* first @a maxlen characters.
*
* @param str1 first string to compare
* @param str2 second string to compare
* @param maxlen maximum number of characters to compare
* @return non-zero if two strings are equal, zero otherwise.
*/
int
MHD_str_equal_caseless_n_ (const char *const str1,
const char *const str2,
size_t maxlen)
{
size_t i;
for (i = 0; i < maxlen; ++i)
{
const char c1 = str1[i];
const char c2 = str2[i];
if (0 == c2)
return 0 == c1;
if ( (c1 != c2) &&
(toasciilower (c1) != toasciilower (c2)) )
return 0;
}
return ! 0;
}
/**
* Check whether @a str has case-insensitive @a token.
* Token could be surrounded by spaces and tabs and delimited by comma.
* Match succeed if substring between start, end (of string) or comma
* contains only case-insensitive token and optional spaces and tabs.
* @warning token must not contain null-charters except optional
* terminating null-character.
* @param str the string to check
* @param token the token to find
* @param token_len length of token, not including optional terminating
* null-character.
* @return non-zero if two strings are equal, zero otherwise.
*/
bool
MHD_str_has_token_caseless_ (const char *str,
const char *const token,
size_t token_len)
{
if (0 == token_len)
return false;
while (0 != *str)
{
size_t i;
/* Skip all whitespaces and empty tokens. */
while (' ' == *str || '\t' == *str || ',' == *str)
str++;
/* Check for token match. */
i = 0;
while (1)
{
const char sc = *(str++);
const char tc = token[i++];
if (0 == sc)
return false;
if ( (sc != tc) &&
(toasciilower (sc) != toasciilower (tc)) )
break;
if (i >= token_len)
{
/* Check whether substring match token fully or
* has additional unmatched chars at tail. */
while (' ' == *str || '\t' == *str)
str++;
/* End of (sub)string? */
if ((0 == *str) || (',' == *str) )
return true;
/* Unmatched chars at end of substring. */
break;
}
}
/* Find next substring. */
while (0 != *str && ',' != *str)
str++;
}
return false;
}
#ifndef MHD_FAVOR_SMALL_CODE
/* Use individual function for each case */
/**
* Convert decimal US-ASCII digits in string to number in uint64_t.
* Conversion stopped at first non-digit character.
*
* @param str string to convert
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_str_to_uint64_ (const char *str,
uint64_t *out_val)
{
const char *const start = str;
uint64_t res;
if (! str || ! out_val || ! isasciidigit (str[0]))
return 0;
res = 0;
do
{
const int digit = (unsigned char) (*str) - '0';
if ( (res > (UINT64_MAX / 10)) ||
( (res == (UINT64_MAX / 10)) &&
((uint64_t) digit > (UINT64_MAX % 10)) ) )
return 0;
res *= 10;
res += digit;
str++;
} while (isasciidigit (*str));
*out_val = res;
return str - start;
}
/**
* Convert not more then @a maxlen decimal US-ASCII digits in string to
* number in uint64_t.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
*
* @param str string to convert
* @param maxlen maximum number of characters to process
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_str_to_uint64_n_ (const char *str,
size_t maxlen,
uint64_t *out_val)
{
uint64_t res;
size_t i;
if (! str || ! maxlen || ! out_val || ! isasciidigit (str[0]))
return 0;
res = 0;
i = 0;
do
{
const int digit = (unsigned char) str[i] - '0';
if ( (res > (UINT64_MAX / 10)) ||
( (res == (UINT64_MAX / 10)) &&
((uint64_t) digit > (UINT64_MAX % 10)) ) )
return 0;
res *= 10;
res += digit;
i++;
} while ( (i < maxlen) &&
isasciidigit (str[i]) );
*out_val = res;
return i;
}
/**
* Convert hexadecimal US-ASCII digits in string to number in uint32_t.
* Conversion stopped at first non-digit character.
*
* @param str string to convert
* @param[out] out_val pointer to uint32_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint32_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint32_ (const char *str,
uint32_t *out_val)
{
const char *const start = str;
uint32_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
digit = toxdigitvalue (*str);
while (digit >= 0)
{
if ( (res < (UINT32_MAX / 16)) ||
((res == (UINT32_MAX / 16)) && ( (uint32_t) digit <= (UINT32_MAX
% 16)) ) )
{
res *= 16;
res += digit;
}
else
return 0;
str++;
digit = toxdigitvalue (*str);
}
if (str - start > 0)
*out_val = res;
return str - start;
}
/**
* Convert not more then @a maxlen hexadecimal US-ASCII digits in string
* to number in uint32_t.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
*
* @param str string to convert
* @param maxlen maximum number of characters to process
* @param[out] out_val pointer to uint32_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint32_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint32_n_ (const char *str,
size_t maxlen,
uint32_t *out_val)
{
size_t i;
uint32_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
i = 0;
while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
{
if ( (res > (UINT32_MAX / 16)) ||
((res == (UINT32_MAX / 16)) && ( (uint32_t) digit > (UINT32_MAX
% 16)) ) )
return 0;
res *= 16;
res += digit;
i++;
}
if (i)
*out_val = res;
return i;
}
/**
* Convert hexadecimal US-ASCII digits in string to number in uint64_t.
* Conversion stopped at first non-digit character.
*
* @param str string to convert
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint64_ (const char *str,
uint64_t *out_val)
{
const char *const start = str;
uint64_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
digit = toxdigitvalue (*str);
while (digit >= 0)
{
if ( (res < (UINT64_MAX / 16)) ||
((res == (UINT64_MAX / 16)) && ( (uint64_t) digit <= (UINT64_MAX
% 16)) ) )
{
res *= 16;
res += digit;
}
else
return 0;
str++;
digit = toxdigitvalue (*str);
}
if (str - start > 0)
*out_val = res;
return str - start;
}
/**
* Convert not more then @a maxlen hexadecimal US-ASCII digits in string
* to number in uint64_t.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
*
* @param str string to convert
* @param maxlen maximum number of characters to process
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint64_n_ (const char *str,
size_t maxlen,
uint64_t *out_val)
{
size_t i;
uint64_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
i = 0;
while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
{
if ( (res > (UINT64_MAX / 16)) ||
((res == (UINT64_MAX / 16)) && ( (uint64_t) digit > (UINT64_MAX
% 16)) ) )
return 0;
res *= 16;
res += digit;
i++;
}
if (i)
*out_val = res;
return i;
}
#else /* MHD_FAVOR_SMALL_CODE */
/**
* Generic function for converting not more then @a maxlen
* hexadecimal or decimal US-ASCII digits in string to number.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
* To be used only within macro.
*
* @param str the string to convert
* @param maxlen the maximum number of characters to process
* @param out_val the pointer to variable to store result of conversion
* @param val_size the size of variable pointed by @a out_val, in bytes, 4 or 8
* @param max_val the maximum decoded number
* @param base the numeric base, 10 or 16
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then @max_val, @val_size is not 16/32 or @a out_val is NULL
*/
size_t
MHD_str_to_uvalue_n_ (const char *str,
size_t maxlen,
void *out_val,
size_t val_size,
uint64_t max_val,
int base)
{
size_t i;
uint64_t res;
int digit;
const uint64_t max_v_div_b = max_val / base;
const uint64_t max_v_mod_b = max_val % base;
/* 'digit->value' must be function, not macro */
int (*const dfunc)(char) = (base == 16) ?
toxdigitvalue : todigitvalue;
if (! str || ! out_val ||
((base != 16) && (base != 10)) )
return 0;
res = 0;
i = 0;
while (maxlen > i && 0 <= (digit = dfunc (str[i])))
{
if ( ((max_v_div_b) < res) ||
(( (max_v_div_b) == res) && ( (max_v_mod_b) < (uint64_t) digit) ) )
return 0;
res *= base;
res += digit;
i++;
}
if (i)
{
if (8 == val_size)
*(uint64_t *) out_val = res;
else if (4 == val_size)
*(uint32_t *) out_val = (uint32_t) res;
else
return 0;
}
return i;
}
#endif /* MHD_FAVOR_SMALL_CODE */