892 lines
18 KiB
C
892 lines
18 KiB
C
#include "include/conf.h"
|
|
|
|
/* TODO: Remove _POSIX_C_SOURCE */
|
|
|
|
#ifndef _POSIX_C_SOURCE
|
|
# define _POSIX_C_SOURCE 199309L
|
|
#endif /* _POSIX_C_SOURCE */
|
|
|
|
#include "include/def.h"
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#ifdef VS_NO_GETRANDOM
|
|
# include "include/file.h"
|
|
# include "include/vessel.h"
|
|
|
|
static File urandom_f;
|
|
static bool urandom_initialized = false;
|
|
|
|
static void get_rand_bytes_close_fd(void) {
|
|
if (urandom_initialized) {
|
|
flog_info(&vessel_lg, "Closing /dev/urandom file: %ju.", File_identity(&urandom_f));
|
|
File_destroy(&urandom_f);
|
|
}
|
|
}
|
|
|
|
#else
|
|
# include <sys/random.h>
|
|
#endif /* VS_NO_GETRANDOM */
|
|
|
|
bool vs_bool_xor(bool first, bool second) { return first ^ second; }
|
|
|
|
char *vs_dupnstr(const char *src, size_t len) {
|
|
if (!src) {
|
|
return NULL;
|
|
}
|
|
|
|
char *dst = VS_MALLOC((len + 1) * sizeof(*dst));
|
|
|
|
if (dst) {
|
|
memcpy(dst, src, len);
|
|
dst[len] = '\0';
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
char *vs_dupstr(const char *src) {
|
|
if (!src) {
|
|
return NULL;
|
|
}
|
|
|
|
const size_t len = strlen(src);
|
|
char *dst = VS_MALLOC((len + 1) * sizeof(*dst));
|
|
|
|
if (dst) {
|
|
memcpy(dst, src, len);
|
|
dst[len] = '\0';
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
void *vs_dupmem(const void *src, size_t len) {
|
|
if (!src || 0 == len) {
|
|
return NULL;
|
|
}
|
|
|
|
void *dst = VS_MALLOC(len);
|
|
|
|
if (dst) {
|
|
memcpy(dst, src, len);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
wchar_t *vs_wdupnstr(const wchar_t *src, size_t len) {
|
|
if (!src) {
|
|
return NULL;
|
|
}
|
|
|
|
wchar_t *dst = VS_MALLOC((len + 1) * sizeof(*dst));
|
|
|
|
if (dst) {
|
|
memcpy(dst, src, len);
|
|
dst[len] = L'\0';
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
wchar_t *vs_wdupstr(const wchar_t *src) {
|
|
if (!src) {
|
|
return NULL;
|
|
}
|
|
|
|
size_t len = wcslen(src);
|
|
|
|
wchar_t *dst = VS_MALLOC((len + 1) * sizeof(*dst));
|
|
if (dst) {
|
|
memcpy(dst, src, len * sizeof(*dst));
|
|
dst[len] = L'\0';
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
char *vs_lowstr(char *src) {
|
|
if (src) {
|
|
while (*src) {
|
|
*src = (char)tolower((unsigned char)*src);
|
|
++src;
|
|
}
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
char *vs_lownstr(char *src, size_t len) {
|
|
if (src) {
|
|
for (size_t idx = 0; idx < len && src[idx] != '\0'; ++idx) {
|
|
src[idx] = (char)tolower((unsigned char)src[idx]);
|
|
}
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
char *vs_uppstr(char *src) {
|
|
if (!src) {
|
|
return NULL;
|
|
}
|
|
|
|
while (*src) {
|
|
*src = (char)toupper((unsigned char)*src);
|
|
++src;
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
char *vs_uppnstr(char *src, size_t len) {
|
|
if (src) {
|
|
for (size_t idx = 0; idx < len && src[idx] != '\0'; ++idx) {
|
|
src[idx] = (char)toupper((unsigned char)src[idx]);
|
|
}
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
bool vs_startstr(const char *src, const char *prefix, bool sensitive) {
|
|
if (!src || !prefix) {
|
|
return false;
|
|
}
|
|
|
|
const size_t prefix_len = strlen(prefix);
|
|
|
|
if (prefix_len > strlen(src)) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t idx = 0; idx < prefix_len; ++idx) {
|
|
if (sensitive) {
|
|
if (src[idx] != prefix[idx]) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (tolower(src[idx]) != tolower(prefix[idx])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t vs_get_rand_bytes(void *buf, size_t buflen) {
|
|
#ifdef VS_NO_GETRANDOM
|
|
if (!urandom_initialized) {
|
|
if (!File_init(&urandom_f, NULL)) {
|
|
return false;
|
|
}
|
|
|
|
if (!File_open(&urandom_f, "/dev/urandom", FILEF_RD, FILEM_ALL)) {
|
|
File_destroy(&urandom_f);
|
|
return false;
|
|
}
|
|
|
|
atexit(get_rand_bytes_close_fd);
|
|
|
|
urandom_initialized = true;
|
|
}
|
|
|
|
return File_read(&urandom_f, buf, buflen);
|
|
#else
|
|
const ssize_t count = getrandom(buf, buflen, 0);
|
|
return count < 1 ? 0 : (size_t)count;
|
|
#endif /* NO_GETRANDOM */
|
|
}
|
|
|
|
uint64_t vs_get_rand(void) {
|
|
uint64_t num = (uint64_t)14913935173710576959ULL; /* random prime number as a fallback */
|
|
vs_get_rand_bytes(&num, sizeof(num));
|
|
return num;
|
|
}
|
|
|
|
void vs_secsleep(double sleep_s) {
|
|
struct timespec request;
|
|
|
|
request.tv_sec = (time_t)sleep_s;
|
|
request.tv_nsec = (long)((sleep_s - (double)request.tv_sec) * 1e9);
|
|
|
|
nanosleep(&request, NULL);
|
|
}
|
|
|
|
uint64_t vs_hex2u64(const char *src) {
|
|
if (!src || !*src) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t result = 0;
|
|
|
|
while (*src) {
|
|
uint8_t digit = 0;
|
|
|
|
if (*src >= '0' && *src <= '9') {
|
|
digit = (uint8_t)*src - '0';
|
|
} else if (*src >= 'a' && *src <= 'f') {
|
|
digit = 10 + (uint8_t)(*src - 'a');
|
|
} else if (*src >= 'A' && *src <= 'F') {
|
|
digit = 10 + (uint8_t)(*src - 'A');
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (result > (UINT64_MAX >> (uint64_t)4)) {
|
|
return UINT64_MAX;
|
|
}
|
|
|
|
result = (result << (uint64_t)4) | digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint64_t vs_hexn2u64(const char *src, size_t len) {
|
|
if (!src || !len) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t result = 0;
|
|
|
|
while (len--) {
|
|
uint8_t digit = 0;
|
|
|
|
if (*src >= '0' && *src <= '9') {
|
|
digit = (uint8_t)*src - '0';
|
|
} else if (*src >= 'a' && *src <= 'f') {
|
|
digit = 10 + (uint8_t)(*src - 'a');
|
|
} else if (*src >= 'A' && *src <= 'F') {
|
|
digit = 10 + (uint8_t)(*src - 'A');
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (result > (UINT64_MAX >> (uint64_t)4)) {
|
|
return UINT64_MAX;
|
|
}
|
|
|
|
result = (result << (uint64_t)4) | digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
size_t vs_hex2usize(const char *src) {
|
|
if (!src || !*src) {
|
|
return 0;
|
|
}
|
|
|
|
size_t result = 0;
|
|
|
|
while (*src) {
|
|
uint8_t digit = 0;
|
|
|
|
if (*src >= '0' && *src <= '9') {
|
|
digit = (uint8_t)*src - '0';
|
|
} else if (*src >= 'a' && *src <= 'f') {
|
|
digit = 10 + (uint8_t)(*src - 'a');
|
|
} else if (*src >= 'A' && *src <= 'F') {
|
|
digit = 10 + (uint8_t)(*src - 'A');
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (result > (SIZE_MAX >> (size_t)4)) {
|
|
return SIZE_MAX;
|
|
}
|
|
|
|
result = (result << (size_t)4) | digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
size_t vs_hexn2usize(const char *src, size_t len) {
|
|
if (!src || !len) {
|
|
return 0;
|
|
}
|
|
|
|
size_t result = 0;
|
|
|
|
while (len--) {
|
|
uint8_t digit = 0;
|
|
|
|
if (*src >= '0' && *src <= '9') {
|
|
digit = (uint8_t)*src - '0';
|
|
} else if (*src >= 'a' && *src <= 'f') {
|
|
digit = 10 + (uint8_t)(*src - 'a');
|
|
} else if (*src >= 'A' && *src <= 'F') {
|
|
digit = 10 + (uint8_t)(*src - 'A');
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (result > (SIZE_MAX >> (size_t)4)) {
|
|
return SIZE_MAX;
|
|
}
|
|
|
|
result = (result << (size_t)4) | digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint64_t vs_str2u64(const char *src) {
|
|
if (!src || !*src) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t result = 0;
|
|
|
|
while (*src >= '0' && *src <= '9') {
|
|
uint64_t digit = (uint64_t)(*src - '0');
|
|
|
|
if (result > (UINT64_MAX - digit) / 10) {
|
|
return UINT64_MAX;
|
|
}
|
|
|
|
result = (result * 10) + digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint64_t vs_strn2u64(const char *src, size_t len) {
|
|
if (!src || !len) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t result = 0;
|
|
|
|
while (len-- && *src >= '0' && *src <= '9') {
|
|
uint64_t digit = (uint64_t)(*src - '0');
|
|
|
|
if (result > (UINT64_MAX - digit) / 10) {
|
|
return UINT64_MAX;
|
|
}
|
|
|
|
result = (result * 10) + digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
size_t vs_str2usize(const char *src) {
|
|
if (!src || !*src) {
|
|
return 0;
|
|
}
|
|
|
|
size_t result = 0;
|
|
|
|
while (*src >= '0' && *src <= '9') {
|
|
size_t digit = (size_t)(*src - '0');
|
|
|
|
if (result > (SIZE_MAX - digit) / 10) {
|
|
return SIZE_MAX;
|
|
}
|
|
|
|
result = (result * 10) + digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
size_t vs_strn2usize(const char *src, size_t len) {
|
|
if (!src || !len) {
|
|
return 0;
|
|
}
|
|
|
|
size_t result = 0;
|
|
|
|
while (len-- && *src >= '0' && *src <= '9') {
|
|
size_t digit = (size_t)(*src - '0');
|
|
|
|
if (result > (SIZE_MAX - digit) / 10) {
|
|
return SIZE_MAX;
|
|
}
|
|
|
|
result = (result * 10) + digit;
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint16_t vs_str2u16(const char *src) {
|
|
if (!src || !*src) {
|
|
return 0;
|
|
}
|
|
|
|
uint16_t result = 0;
|
|
|
|
while (*src >= '0' && *src <= '9') {
|
|
uint16_t digit = (uint16_t)(*src - '0');
|
|
|
|
if (result > (UINT16_MAX - digit) / 10) {
|
|
return UINT16_MAX;
|
|
}
|
|
|
|
result = (uint16_t)((result * 10) + digit);
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint16_t vs_strn2u16(const char *src, size_t len) {
|
|
if (!src || !len) {
|
|
return 0;
|
|
}
|
|
|
|
uint16_t result = 0;
|
|
|
|
while (len-- && *src >= '0' && *src <= '9') {
|
|
uint16_t digit = (uint16_t)(*src - '0');
|
|
|
|
if (result > (UINT16_MAX - digit) / 10) {
|
|
return UINT16_MAX;
|
|
}
|
|
|
|
result = (uint16_t)((result * 10) + digit);
|
|
++src;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
intmax_t vs_str2max(const char *src) {
|
|
if (!src || !*src) {
|
|
return 0;
|
|
}
|
|
|
|
int sign = 1;
|
|
if (*src == '-') {
|
|
sign = -1;
|
|
++src;
|
|
} else if (*src == '+') {
|
|
++src;
|
|
}
|
|
|
|
intmax_t result = 0;
|
|
|
|
while (*src >= '0' && *src <= '9') {
|
|
intmax_t digit = (intmax_t)(*src - '0');
|
|
|
|
if (sign == 1 && result > (INTMAX_MAX - digit) / 10) {
|
|
return INTMAX_MAX;
|
|
}
|
|
|
|
if (sign == -1 && result > (-(INTMAX_MIN + digit)) / 10) {
|
|
return INTMAX_MIN;
|
|
}
|
|
|
|
result = (result * 10) + digit;
|
|
++src;
|
|
}
|
|
|
|
return result * sign;
|
|
}
|
|
|
|
intmax_t vs_strn2max(const char *src, size_t len) {
|
|
if (!src || !len) {
|
|
return 0;
|
|
}
|
|
|
|
int sign = 1;
|
|
if (*src == '-' && len > 1) {
|
|
sign = -1;
|
|
++src;
|
|
--len;
|
|
} else if (*src == '+' && len > 1) {
|
|
++src;
|
|
--len;
|
|
}
|
|
|
|
intmax_t result = 0;
|
|
while (len-- && *src >= '0' && *src <= '9') {
|
|
intmax_t digit = *src - '0';
|
|
|
|
if (sign == 1 && result > (INTMAX_MAX - digit) / 10) {
|
|
return INTMAX_MAX;
|
|
}
|
|
|
|
if (sign == -1 && result > (-(INTMAX_MIN + digit)) / 10) {
|
|
return INTMAX_MIN;
|
|
}
|
|
|
|
result = (result * 10) + digit;
|
|
++src;
|
|
}
|
|
|
|
return result * sign;
|
|
}
|
|
|
|
char *vs_escstrncpy(char *dest, const char *src, size_t len) {
|
|
if (!dest || !src || 0 == len) {
|
|
return NULL;
|
|
}
|
|
|
|
size_t idx = 0;
|
|
bool literal_next = false;
|
|
|
|
while (idx < len && *src) {
|
|
if (literal_next) {
|
|
dest[idx++] = *src;
|
|
literal_next = false;
|
|
} else if (*src == '\\') {
|
|
literal_next = true;
|
|
} else {
|
|
dest[idx++] = *src;
|
|
}
|
|
|
|
++src;
|
|
}
|
|
|
|
if (idx < len) {
|
|
dest[idx] = '\0';
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
char *vs_escstrcpy(char *dest, const char *src) {
|
|
if (!dest || !src) {
|
|
return NULL;
|
|
}
|
|
|
|
size_t idx = 0;
|
|
bool literal_next = false;
|
|
|
|
while (*src) {
|
|
if (literal_next) {
|
|
dest[idx++] = *src;
|
|
literal_next = false;
|
|
} else if (*src == '\\') {
|
|
literal_next = true;
|
|
} else {
|
|
dest[idx++] = *src;
|
|
}
|
|
++src;
|
|
}
|
|
|
|
dest[idx] = '\0';
|
|
return dest;
|
|
}
|
|
|
|
char *vs_strsafe(char *str, const char *allowlist, size_t allowlist_size, bool allow_uppercase) {
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
|
|
char *write_ptr = str;
|
|
|
|
for (char *read_ptr = str; *read_ptr; ++read_ptr) {
|
|
if (!allow_uppercase && isupper(*read_ptr)) {
|
|
++read_ptr;
|
|
continue;
|
|
}
|
|
|
|
if (isalnum(*read_ptr)) {
|
|
*write_ptr++ = *read_ptr;
|
|
} else {
|
|
if (!allowlist || allowlist_size == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (uint64_t idx = 0; idx < allowlist_size; ++idx) {
|
|
if (*read_ptr == allowlist[idx]) {
|
|
*write_ptr++ = *read_ptr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*write_ptr = '\0';
|
|
|
|
return str;
|
|
}
|
|
|
|
bool vs_strissafe(const char *str,
|
|
const char *allowlist,
|
|
size_t allowlist_size,
|
|
bool allow_uppercase) {
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
for (const char *ptr = str; *ptr; ++ptr) {
|
|
if (!allow_uppercase && isupper(*ptr)) {
|
|
return false;
|
|
}
|
|
|
|
if (isalnum(*ptr)) {
|
|
continue;
|
|
}
|
|
|
|
bool found_in_allowlist = false;
|
|
|
|
for (size_t idx = 0; idx < allowlist_size; ++idx) {
|
|
if (*ptr == allowlist[idx]) {
|
|
found_in_allowlist = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found_in_allowlist) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
char *vs_strnsafe(char *str, size_t len, const char *allowlist, size_t allowlist_size) {
|
|
if (!str || len == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
char *write_ptr = str;
|
|
size_t chars_written = 0;
|
|
|
|
for (const char *read_ptr = str; *read_ptr && chars_written < len - 1; ++read_ptr) {
|
|
if (isalnum(*read_ptr)) {
|
|
*write_ptr++ = *read_ptr;
|
|
chars_written++;
|
|
} else {
|
|
if (!allowlist || allowlist_size == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (uint64_t idx = 0; idx < allowlist_size; ++idx) {
|
|
if (*read_ptr == allowlist[idx]) {
|
|
*write_ptr++ = *read_ptr;
|
|
chars_written++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*write_ptr = '\0';
|
|
|
|
return str;
|
|
}
|
|
|
|
bool vs_strnissafe(const char *str, size_t len, const char *allowlist, size_t allowlist_size) {
|
|
if (!str || len == 0) {
|
|
return false;
|
|
}
|
|
|
|
size_t chars_checked = 0;
|
|
|
|
for (const char *ptr = str; *ptr && chars_checked < len; ++ptr, ++chars_checked) {
|
|
if (isalnum(*ptr)) {
|
|
continue;
|
|
}
|
|
|
|
bool found_in_allowlist = false;
|
|
|
|
for (size_t idx = 0; idx < allowlist_size; ++idx) {
|
|
if (*ptr == allowlist[idx]) {
|
|
found_in_allowlist = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found_in_allowlist) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vs_strnintonly(const char *str, size_t len) {
|
|
if (!str || len == 0) {
|
|
return false;
|
|
}
|
|
|
|
const char *end = str + len;
|
|
|
|
while (str < end && *str == ' ') {
|
|
++str;
|
|
}
|
|
|
|
if (str < end && (*str == '+' || *str == '-')) {
|
|
++str;
|
|
}
|
|
|
|
const char *start_digits = str;
|
|
while (str < end && *str >= '0' && *str <= '9') {
|
|
++str;
|
|
}
|
|
|
|
if (str == start_digits) {
|
|
return false;
|
|
}
|
|
|
|
while (str < end && *str == ' ') {
|
|
++str;
|
|
}
|
|
|
|
return str == end;
|
|
}
|
|
|
|
bool vs_strintonly(const char *str) {
|
|
if (!str || !*str) {
|
|
return false;
|
|
}
|
|
|
|
while (*str == ' ') {
|
|
++str;
|
|
}
|
|
|
|
if (*str == '+' || *str == '-') {
|
|
++str;
|
|
}
|
|
|
|
const char *start_digits = str;
|
|
while (*str >= '0' && *str <= '9') {
|
|
++str;
|
|
}
|
|
|
|
if (str == start_digits) {
|
|
return false;
|
|
}
|
|
|
|
while (*str == ' ') {
|
|
++str;
|
|
}
|
|
|
|
return *str == '\0';
|
|
}
|
|
|
|
double vs_strn2double(const char *str, size_t len, bool frac_required) {
|
|
if (!str || len == 0) {
|
|
return 0.0;
|
|
}
|
|
|
|
double sign = 1.0;
|
|
double result = 0.0;
|
|
double fraction = 1.0;
|
|
|
|
const char *ptr = str;
|
|
const char *end = str + len;
|
|
|
|
bool has_frac = false;
|
|
|
|
if (*ptr == '-') {
|
|
sign = -1.0;
|
|
++ptr;
|
|
} else if (*ptr == '+') {
|
|
++ptr;
|
|
}
|
|
|
|
while (*ptr >= '0' && *ptr <= '9' && ptr < end) {
|
|
result = (result * 10) + (*ptr - '0');
|
|
++ptr;
|
|
}
|
|
|
|
if (*ptr == '.' && ptr < end) {
|
|
++ptr;
|
|
has_frac = true;
|
|
} else if (!has_frac && frac_required) {
|
|
return 0.0;
|
|
}
|
|
|
|
while (has_frac && *ptr >= '0' && *ptr <= '9' && ptr < end) {
|
|
fraction /= 10.0;
|
|
result += (*ptr - '0') * fraction;
|
|
++ptr;
|
|
}
|
|
|
|
return ptr == end ? result * sign : 0.0;
|
|
}
|
|
|
|
double vs_str2double(const char *str, bool frac_required) {
|
|
if (!str || !*str) {
|
|
return 0.0;
|
|
}
|
|
|
|
double sign = 1.0;
|
|
double result = 0.0;
|
|
double fraction = 1.0;
|
|
|
|
const char *ptr = str;
|
|
bool has_frac = false;
|
|
|
|
if (*ptr == '-') {
|
|
sign = -1.0;
|
|
++ptr;
|
|
} else if (*ptr == '+') {
|
|
++ptr;
|
|
}
|
|
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
result = (result * 10) + (*ptr - '0');
|
|
++ptr;
|
|
}
|
|
|
|
if (*ptr == '.') {
|
|
++ptr;
|
|
has_frac = true;
|
|
} else if (frac_required) {
|
|
return 0.0;
|
|
}
|
|
|
|
while (has_frac && *ptr >= '0' && *ptr <= '9') {
|
|
fraction /= 10.0;
|
|
result += (*ptr - '0') * fraction;
|
|
++ptr;
|
|
}
|
|
|
|
while (*ptr == ' ') {
|
|
++ptr;
|
|
}
|
|
|
|
return *ptr == '\0' ? result * sign : 0.0;
|
|
}
|
|
|
|
unsigned long vs_double_mod(double divident, double divisor) {
|
|
if (divisor < VS_DNULL) {
|
|
abort();
|
|
}
|
|
|
|
return (unsigned long)divident -
|
|
(((unsigned long)(divident / divisor)) * (unsigned long)divisor);
|
|
}
|
|
|
|
bool vs_strip_spaces(const char **start, const char **end) {
|
|
if (!start || !*start || !end || !*end) {
|
|
return false;
|
|
}
|
|
|
|
if (*start > *end) {
|
|
return false;
|
|
}
|
|
|
|
while (*start <= *end && **start == ' ') {
|
|
++*start;
|
|
}
|
|
|
|
while (*end >= *start && *(*end - 1) == ' ') {
|
|
--*end;
|
|
}
|
|
|
|
return *end >= *start;
|
|
}
|