#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "memcached.h"
static char *uriencode_map[256];
static char uriencode_str[768];
void uriencode_init(void) {
int x;
char *str = uriencode_str;
for (x = 0; x < 256; x++) {
if (isalnum(x) || x == '-' || x == '.' || x == '_' || x == '~') {
uriencode_map[x] = NULL;
} else {
snprintf(str, 4, "%%%02hhX", (unsigned char)x);
uriencode_map[x] = str;
str += 3; /* lobbing off the \0 is fine */
}
}
}
bool uriencode(const char *src, char *dst, const size_t srclen, const size_t dstlen) {
int x;
size_t d = 0;
for (x = 0; x < srclen; x++) {
if (d + 4 > dstlen)
return false;
if (uriencode_map[(unsigned char) src[x]] != NULL) {
memcpy(&dst[d], uriencode_map[(unsigned char) src[x]], 3);
d += 3;
} else {
dst[d] = src[x];
d++;
}
}
dst[d] = '\0';
return true;
}
/* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */
#define xisspace(c) isspace((unsigned char)c)
bool safe_strtoull(const char *str, uint64_t *out) {
assert(out != NULL);
errno = 0;
*out = 0;
char *endptr;
unsigned long long ull = strtoull(str, &endptr, 10);
if ((errno == ERANGE) || (str == endptr)) {
return false;
}
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
if ((long long) ull < 0) {
/* only check for negative signs in the uncommon case when
* the unsigned number is so big that it's negative as a
* signed number. */
if (strchr(str, '-') != NULL) {
return false;
}
}
*out = ull;
return true;
}
return false;
}
/* Could macro this. Decided to keep this unrolled for safety rather than add
* the base parameter to all callers. Very few places need to parse a number
* outside of base 10, currently exactly once, so splitting this up should
* help avoid typo bugs.
*/
bool safe_strtoull_hex(const char *str, uint64_t *out) {
assert(out != NULL);
errno = 0;
*out = 0;
char *endptr;
unsigned long long ull = strtoull(str, &endptr, 16);
if ((errno == ERANGE) || (str == endptr)) {
return false;
}
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
if ((long long) ull < 0) {
/* only check for negative signs in the uncommon case when
* the unsigned number is so big that it's negative as a
* signed number. */
if (strchr(str, '-') != NULL) {
return false;
}
}
*out = ull;
return true;
}
return false;
}
bool safe_strtoll(const char *str, int64_t *out) {
assert(out != NULL);
errno = 0;
*out = 0;
char *endptr;
long long ll = strtoll(str, &endptr, 10);
if ((errno == ERANGE) || (str == endptr)) {
return false;
}
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
*out = ll;
return true;
}
return false;
}
bool safe_strtoul(const char *str, uint32_t *out) {
char *endptr = NULL;
unsigned long l = 0;
assert(out);
assert(str);
*out = 0;
errno = 0;
l = strtoul(str, &endptr, 10);
if ((errno == ERANGE) || (str == endptr)) {
return false;
}
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
if ((long) l < 0) {
/* only check for negative signs in the uncommon case when
* the unsigned number is so big that it's negative as a
* signed number. */
if (strchr(str, '-') != NULL) {
return false;
}
}
*out = l;
return true;
}
return false;
}
bool safe_strtol(const char *str, int32_t *out) {
assert(out != NULL);
errno = 0;
*out = 0;
char *endptr;
long l = strtol(str, &endptr, 10);
if ((errno == ERANGE) || (str == endptr)) {
return false;
}
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
*out = l;
return true;
}
return false;
}
bool safe_strtod(const char *str, double *out) {
assert(out != NULL);
errno = 0;
*out = 0;
char *endptr;
double d = strtod(str, &endptr);
if ((errno == ERANGE) || (str == endptr)) {
return false;
}
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
*out = d;
return true;
}
return false;
}
// slow, safe function for copying null terminated buffers.
// ensures null terminator set on destination buffer. copies at most dstmax-1
// non-null bytes.
// Explicitly avoids over-reading src while looking for the null byte.
// returns true if src was fully copied.
// returns false if src was truncated into dst.
bool safe_strcpy(char *dst, const char *src, const size_t dstmax) {
size_t x;
for (x = 0; x < dstmax - 1 && src[x] != '\0'; x++) {
dst[x] = src[x];
}
dst[x] = '\0';
if (src[x] == '\0') {
return true;
} else {
return false;
}
}
bool safe_memcmp(const void *a, const void *b, size_t len) {
const volatile unsigned char *ua = (const volatile unsigned char *)a;
const volatile unsigned char *ub = (const volatile unsigned char *)b;
int delta = 0;
size_t x;
for (x = 0; x < len; x++) {
delta |= ua[x] ^ ub[x];
}
if (delta == 0) {
return true;
} else {
return false;
}
}
void vperror(const char *fmt, ...) {
int old_errno = errno;
char buf[1024];
va_list ap;
va_start(ap, fmt);
if (vsnprintf(buf, sizeof(buf), fmt, ap) == -1) {
buf[sizeof(buf) - 1] = '\0';
}
va_end(ap);
errno = old_errno;
perror(buf);
}
#ifndef HAVE_HTONLL
static uint64_t mc_swap64(uint64_t in) {
#ifdef ENDIAN_LITTLE
/* Little endian, flip the bytes around until someone makes a faster/better
* way to do this. */
int64_t rv = 0;
int i = 0;
for(i = 0; i<8; i++) {
rv = (rv << 8) | (in & 0xff);
in >>= 8;
}
return rv;
#else
/* big-endian machines don't need byte swapping */
return in;
#endif
}
uint64_t ntohll(uint64_t val) {
return mc_swap64(val);
}
uint64_t htonll(uint64_t val) {
return mc_swap64(val);
}
#endif