#include "lib/string/util_string.h"
#include "lib/string/compat_ctype.h"
#include "lib/err/torerr.h"
#include "lib/ctime/di_ops.h"
#include "lib/defs/digest_sizes.h"
#include <string.h>
#include <stdlib.h>
const void *
tor_memmem(const void *_haystack, size_t hlen,
const void *_needle, size_t nlen)
{
#if defined(HAVE_MEMMEM) && (!defined(__GNUC__) || __GNUC__ >= 2)
raw_assert(nlen);
return memmem(_haystack, hlen, _needle, nlen);
#else
const char *p, *last_possible_start;
const char *haystack = (const char*)_haystack;
const char *needle = (const char*)_needle;
char first;
raw_assert(nlen);
if (nlen > hlen)
return NULL;
p = haystack;
last_possible_start = haystack + hlen - nlen;
first = *(const char*)needle;
while ((p = memchr(p, first, last_possible_start + 1 - p))) {
if (fast_memeq(p, needle, nlen))
return p;
if (++p > last_possible_start) {
return NULL;
}
}
return NULL;
#endif
}
const void *
tor_memstr(const void *haystack, size_t hlen, const char *needle)
{
return tor_memmem(haystack, hlen, needle, strlen(needle));
}
int
fast_mem_is_zero(const char *mem, size_t len)
{
static const char ZERO[] = {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
};
while (len >= sizeof(ZERO)) {
if (fast_memcmp(mem, ZERO, sizeof(ZERO)))
return 0;
len -= sizeof(ZERO);
mem += sizeof(ZERO);
}
if (len)
return fast_memeq(mem, ZERO, len);
return 1;
}
int
tor_digest_is_zero(const char *digest)
{
return safe_mem_is_zero(digest, DIGEST_LEN);
}
int
tor_digest256_is_zero(const char *digest)
{
return safe_mem_is_zero(digest, DIGEST256_LEN);
}
void
tor_strstrip(char *s, const char *strip)
{
char *readp = s;
while (*readp) {
if (strchr(strip, *readp)) {
++readp;
} else {
*s++ = *readp++;
}
}
*s = '\0';
}
void
tor_strlower(char *s)
{
while (*s) {
*s = TOR_TOLOWER(*s);
++s;
}
}
void
tor_strupper(char *s)
{
while (*s) {
*s = TOR_TOUPPER(*s);
++s;
}
}
int
tor_strisprint(const char *s)
{
while (*s) {
if (!TOR_ISPRINT(*s))
return 0;
s++;
}
return 1;
}
int
tor_strisnonupper(const char *s)
{
while (*s) {
if (TOR_ISUPPER(*s))
return 0;
s++;
}
return 1;
}
int
tor_strisspace(const char *s)
{
while (*s) {
if (!TOR_ISSPACE(*s))
return 0;
s++;
}
return 1;
}
int
strcmp_opt(const char *s1, const char *s2)
{
if (!s1) {
if (!s2)
return 0;
else
return -1;
} else if (!s2) {
return 1;
} else {
return strcmp(s1, s2);
}
}
int
strcmpstart(const char *s1, const char *s2)
{
size_t n = strlen(s2);
return strncmp(s1, s2, n);
}
int
strcasecmpstart(const char *s1, const char *s2)
{
size_t n = strlen(s2);
return strncasecmp(s1, s2, n);
}
int
fast_memcmpstart(const void *mem, size_t memlen,
const char *prefix)
{
size_t plen = strlen(prefix);
if (memlen < plen)
return -1;
return fast_memcmp(mem, prefix, plen);
}
int
strcmpend(const char *s1, const char *s2)
{
size_t n1 = strlen(s1), n2 = strlen(s2);
if (n2>n1)
return strcmp(s1,s2);
else
return strncmp(s1+(n1-n2), s2, n2);
}
int
strcasecmpend(const char *s1, const char *s2)
{
size_t n1 = strlen(s1), n2 = strlen(s2);
if (n2>n1)
return strcasecmp(s1,s2);
else
return strncasecmp(s1+(n1-n2), s2, n2);
}
const char *
eat_whitespace(const char *s)
{
raw_assert(s);
while (1) {
switch (*s) {
case '\0':
default:
return s;
case ' ':
case '\t':
case '\n':
case '\r':
++s;
break;
case '#':
++s;
while (*s && *s != '\n')
++s;
}
}
}
const char *
eat_whitespace_eos(const char *s, const char *eos)
{
raw_assert(s);
raw_assert(eos && s <= eos);
while (s < eos) {
switch (*s) {
case '\0':
default:
return s;
case ' ':
case '\t':
case '\n':
case '\r':
++s;
break;
case '#':
++s;
while (s < eos && *s && *s != '\n')
++s;
}
}
return s;
}
const char *
eat_whitespace_no_nl(const char *s)
{
while (*s == ' ' || *s == '\t' || *s == '\r')
++s;
return s;
}
const char *
eat_whitespace_eos_no_nl(const char *s, const char *eos)
{
while (s < eos && (*s == ' ' || *s == '\t' || *s == '\r'))
++s;
return s;
}
const char *
find_whitespace(const char *s)
{
while (1) {
switch (*s)
{
case '\0':
case '#':
case ' ':
case '\r':
case '\n':
case '\t':
return s;
default:
++s;
}
}
}
const char *
find_whitespace_eos(const char *s, const char *eos)
{
while (s < eos) {
switch (*s)
{
case '\0':
case '#':
case ' ':
case '\r':
case '\n':
case '\t':
return s;
default:
++s;
}
}
return s;
}
const char *
find_str_at_start_of_line(const char *haystack, const char *needle)
{
size_t needle_len = strlen(needle);
do {
if (!strncmp(haystack, needle, needle_len))
return haystack;
haystack = strchr(haystack, '\n');
if (!haystack)
return NULL;
else
++haystack;
} while (*haystack);
return NULL;
}
int
string_is_C_identifier(const char *string)
{
size_t iter;
size_t length = strlen(string);
if (!length)
return 0;
for (iter = 0; iter < length ; iter++) {
if (iter == 0) {
if (!(TOR_ISALPHA(string[iter]) ||
string[iter] == '_'))
return 0;
} else {
if (!(TOR_ISALPHA(string[iter]) ||
TOR_ISDIGIT(string[iter]) ||
string[iter] == '_'))
return 0;
}
}
return 1;
}
#define TOP_BITS(x) ((uint8_t)(0xFF << (8 - (x))))
#define LOW_BITS(x) ((uint8_t)(0xFF >> (8 - (x))))
static uint8_t
bytes_in_char(uint8_t b)
{
if ((TOP_BITS(1) & b) == 0x00)
return 1; if ((TOP_BITS(3) & b) == TOP_BITS(2))
return 2; if ((TOP_BITS(4) & b) == TOP_BITS(3))
return 3; if ((TOP_BITS(5) & b) == TOP_BITS(4))
return 4;
return 0;
}
static bool
is_continuation_byte(uint8_t b)
{
uint8_t top2bits = b & TOP_BITS(2);
return top2bits == TOP_BITS(1);
}
static bool
validate_char(const uint8_t *c, uint8_t len)
{
if (len == 1)
return true;
uint8_t mask = LOW_BITS(7 - len); uint32_t codepoint = c[0] & mask;
mask = LOW_BITS(6); for (uint8_t i = 1; i < len; i++) {
if (!is_continuation_byte(c[i]))
return false;
codepoint <<= 6;
codepoint |= (c[i] & mask);
}
if (len == 2 && codepoint <= 0x7f)
return false;
if (len == 3 && codepoint <= 0x7ff)
return false;
if (len == 4 && codepoint <= 0xffff)
return false;
if (codepoint >= 0xd800 && codepoint <= 0xdfff)
return false;
return codepoint <= 0x10ffff; }
int
string_is_utf8(const char *str, size_t len)
{
if (!str) {
if (len) {
tor_log_err_sigsafe(
"BUG: string_is_utf8() called with NULL str but non-zero len.");
return false;
}
return true;
}
for (size_t i = 0; i < len;) {
uint8_t num_bytes = bytes_in_char(str[i]);
if (num_bytes == 0) return false;
size_t next_char = i + num_bytes;
if (next_char > len)
return false;
if (!validate_char((const uint8_t*)&str[i], num_bytes))
return false;
i = next_char;
}
return true;
}
int
string_is_utf8_no_bom(const char *str, size_t len)
{
if (str && len >= 3 && (!strcmpstart(str, "\uFEFF") ||
!strcmpstart(str, "\uFFFE"))) {
return false;
}
return string_is_utf8(str, len);
}