version 1.1, 2013/10/14 10:32:47
|
version 1.1.1.3, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "buffer.h" |
#include "buffer.h" |
|
|
#include <stdlib.h> |
#include <stdlib.h> |
Line 7
|
Line 9
|
#include <assert.h> |
#include <assert.h> |
#include <ctype.h> |
#include <ctype.h> |
|
|
#if defined HAVE_STDINT_H |
|
# include <stdint.h> |
|
#elif defined HAVE_INTTYPES_H |
|
# include <inttypes.h> |
|
#endif |
|
|
|
static const char hex_chars[] = "0123456789abcdef"; |
static const char hex_chars[] = "0123456789abcdef"; |
|
|
|
|
/** |
/** |
* init the buffer |
* init the buffer |
* |
* |
Line 25 buffer* buffer_init(void) {
|
Line 20 buffer* buffer_init(void) {
|
buffer *b; |
buffer *b; |
|
|
b = malloc(sizeof(*b)); |
b = malloc(sizeof(*b)); |
assert(b); | force_assert(b); |
|
|
b->ptr = NULL; |
b->ptr = NULL; |
b->size = 0; |
b->size = 0; |
Line 34 buffer* buffer_init(void) {
|
Line 29 buffer* buffer_init(void) {
|
return b; |
return b; |
} |
} |
|
|
buffer *buffer_init_buffer(buffer *src) { | buffer *buffer_init_buffer(const buffer *src) { |
buffer *b = buffer_init(); |
buffer *b = buffer_init(); |
buffer_copy_string_buffer(b, src); | buffer_copy_buffer(b, src); |
return b; |
return b; |
} |
} |
|
|
/** | buffer *buffer_init_string(const char *str) { |
* free the buffer | buffer *b = buffer_init(); |
* | buffer_copy_string(b, str); |
*/ | return b; |
| } |
|
|
void buffer_free(buffer *b) { |
void buffer_free(buffer *b) { |
if (!b) return; | if (NULL == b) return; |
|
|
free(b->ptr); |
free(b->ptr); |
free(b); |
free(b); |
} |
} |
|
|
void buffer_reset(buffer *b) { |
void buffer_reset(buffer *b) { |
if (!b) return; | if (NULL == b) return; |
|
|
/* limit don't reuse buffer larger than ... bytes */ |
/* limit don't reuse buffer larger than ... bytes */ |
if (b->size > BUFFER_MAX_REUSE_SIZE) { |
if (b->size > BUFFER_MAX_REUSE_SIZE) { |
free(b->ptr); |
free(b->ptr); |
b->ptr = NULL; |
b->ptr = NULL; |
b->size = 0; |
b->size = 0; |
} else if (b->size) { | } else if (b->size > 0) { |
b->ptr[0] = '\0'; |
b->ptr[0] = '\0'; |
} |
} |
|
|
b->used = 0; |
b->used = 0; |
} |
} |
|
|
|
void buffer_move(buffer *b, buffer *src) { |
|
buffer tmp; |
|
|
/** | if (NULL == b) { |
* | buffer_reset(src); |
* allocate (if neccessary) enough space for 'size' bytes and | return; |
* set the 'used' counter to 0 | } |
* | buffer_reset(b); |
*/ | if (NULL == src) return; |
|
|
|
tmp = *src; *src = *b; *b = tmp; |
|
} |
|
|
#define BUFFER_PIECE_SIZE 64 |
#define BUFFER_PIECE_SIZE 64 |
|
static size_t buffer_align_size(size_t size) { |
|
size_t align = BUFFER_PIECE_SIZE - (size % BUFFER_PIECE_SIZE); |
|
/* overflow on unsinged size_t is defined to wrap around */ |
|
if (size + align < size) return size; |
|
return size + align; |
|
} |
|
|
int buffer_prepare_copy(buffer *b, size_t size) { | /* make sure buffer is at least "size" big. discard old data */ |
if (!b) return -1; | static void buffer_alloc(buffer *b, size_t size) { |
| force_assert(NULL != b); |
| if (0 == size) size = 1; |
|
|
if ((0 == b->size) || | if (size <= b->size) return; |
(size > b->size)) { | |
if (b->size) free(b->ptr); | |
|
|
b->size = size; | if (NULL != b->ptr) free(b->ptr); |
|
|
/* always allocate a multiply of BUFFER_PIECE_SIZE */ |
|
b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); |
|
|
|
b->ptr = malloc(b->size); |
|
assert(b->ptr); |
|
} |
|
b->used = 0; |
b->used = 0; |
return 0; | b->size = buffer_align_size(size); |
| b->ptr = malloc(b->size); |
| |
| force_assert(NULL != b->ptr); |
} |
} |
|
|
/** | /* make sure buffer is at least "size" big. keep old data */ |
* | static void buffer_realloc(buffer *b, size_t size) { |
* increase the internal buffer (if neccessary) to append another 'size' byte | force_assert(NULL != b); |
* ->used isn't changed | if (0 == size) size = 1; |
* | |
*/ | |
|
|
int buffer_prepare_append(buffer *b, size_t size) { | if (size <= b->size) return; |
if (!b) return -1; | |
|
|
if (0 == b->size) { | b->size = buffer_align_size(size); |
b->size = size; | b->ptr = realloc(b->ptr, b->size); |
|
|
/* always allocate a multiply of BUFFER_PIECE_SIZE */ | force_assert(NULL != b->ptr); |
b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); | } |
|
|
b->ptr = malloc(b->size); |
|
b->used = 0; |
|
assert(b->ptr); |
|
} else if (b->used + size > b->size) { |
|
b->size += size; |
|
|
|
/* always allocate a multiply of BUFFER_PIECE_SIZE */ | char* buffer_string_prepare_copy(buffer *b, size_t size) { |
b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); | force_assert(NULL != b); |
| force_assert(size + 1 > size); |
|
|
b->ptr = realloc(b->ptr, b->size); | buffer_alloc(b, size + 1); |
assert(b->ptr); | |
} | |
return 0; | |
} | |
|
|
int buffer_copy_string(buffer *b, const char *s) { | b->used = 1; |
size_t s_len; | b->ptr[0] = '\0'; |
|
|
if (!s || !b) return -1; | return b->ptr; |
| } |
|
|
s_len = strlen(s) + 1; | char* buffer_string_prepare_append(buffer *b, size_t size) { |
buffer_prepare_copy(b, s_len); | force_assert(NULL != b); |
|
|
memcpy(b->ptr, s, s_len); | if (buffer_string_is_empty(b)) { |
b->used = s_len; | return buffer_string_prepare_copy(b, size); |
| } else { |
| size_t req_size = b->used + size; |
|
|
return 0; | /* not empty, b->used already includes a terminating 0 */ |
} | force_assert(req_size >= b->used); |
|
|
int buffer_copy_string_len(buffer *b, const char *s, size_t s_len) { | /* check for overflow: unsigned overflow is defined to wrap around */ |
if (!s || !b) return -1; | force_assert(req_size >= b->used); |
#if 0 | |
/* removed optimization as we have to keep the empty string | |
* in some cases for the config handling | |
* | |
* url.access-deny = ( "" ) | |
*/ | |
if (s_len == 0) return 0; | |
#endif | |
buffer_prepare_copy(b, s_len + 1); | |
|
|
memcpy(b->ptr, s, s_len); | buffer_realloc(b, req_size); |
b->ptr[s_len] = '\0'; | |
b->used = s_len + 1; | |
|
|
return 0; | return b->ptr + b->used - 1; |
| } |
} |
} |
|
|
int buffer_copy_string_buffer(buffer *b, const buffer *src) { | void buffer_string_set_length(buffer *b, size_t len) { |
if (!src) return -1; | force_assert(NULL != b); |
| force_assert(len + 1 > len); |
|
|
if (src->used == 0) { | buffer_realloc(b, len + 1); |
buffer_reset(b); | |
return 0; | b->used = len + 1; |
} | b->ptr[len] = '\0'; |
return buffer_copy_string_len(b, src->ptr, src->used - 1); | |
} |
} |
|
|
int buffer_append_string(buffer *b, const char *s) { | void buffer_commit(buffer *b, size_t size) |
size_t s_len; | { |
| force_assert(NULL != b); |
| force_assert(b->size > 0); |
|
|
if (!s || !b) return -1; | if (0 == b->used) b->used = 1; |
|
|
s_len = strlen(s); | if (size > 0) { |
buffer_prepare_append(b, s_len + 1); | /* check for overflow: unsigned overflow is defined to wrap around */ |
if (b->used == 0) | force_assert(b->used + size > b->used); |
b->used++; | |
|
|
memcpy(b->ptr + b->used - 1, s, s_len + 1); | force_assert(b->used + size <= b->size); |
b->used += s_len; | b->used += size; |
| } |
|
|
return 0; | b->ptr[b->used - 1] = '\0'; |
} |
} |
|
|
int buffer_append_string_rfill(buffer *b, const char *s, size_t maxlen) { | void buffer_copy_string(buffer *b, const char *s) { |
size_t s_len; | buffer_copy_string_len(b, s, NULL != s ? strlen(s) : 0); |
| } |
|
|
if (!s || !b) return -1; | void buffer_copy_string_len(buffer *b, const char *s, size_t s_len) { |
| force_assert(NULL != b); |
| force_assert(NULL != s || s_len == 0); |
|
|
s_len = strlen(s); | buffer_string_prepare_copy(b, s_len); |
if (s_len > maxlen) s_len = maxlen; | |
buffer_prepare_append(b, maxlen + 1); | |
if (b->used == 0) | |
b->used++; | |
|
|
memcpy(b->ptr + b->used - 1, s, s_len); | if (0 != s_len) memcpy(b->ptr, s, s_len); |
if (maxlen > s_len) { | |
memset(b->ptr + b->used - 1 + s_len, ' ', maxlen - s_len); | buffer_commit(b, s_len); |
| } |
| |
| void buffer_copy_buffer(buffer *b, const buffer *src) { |
| if (NULL == src || 0 == src->used) { |
| buffer_string_prepare_copy(b, 0); |
| b->used = 0; /* keep special empty state for now */ |
| } else { |
| buffer_copy_string_len(b, src->ptr, buffer_string_length(src)); |
} |
} |
|
} |
|
|
b->used += maxlen; | void buffer_append_string(buffer *b, const char *s) { |
b->ptr[b->used - 1] = '\0'; | buffer_append_string_len(b, s, NULL != s ? strlen(s) : 0); |
return 0; | |
} |
} |
|
|
/** |
/** |
Line 218 int buffer_append_string_rfill(buffer *b, const char *
|
Line 213 int buffer_append_string_rfill(buffer *b, const char *
|
* @param s_len size of the string (without the terminating \0) |
* @param s_len size of the string (without the terminating \0) |
*/ |
*/ |
|
|
int buffer_append_string_len(buffer *b, const char *s, size_t s_len) { | void buffer_append_string_len(buffer *b, const char *s, size_t s_len) { |
if (!s || !b) return -1; | char *target_buf; |
if (s_len == 0) return 0; | |
|
|
buffer_prepare_append(b, s_len + 1); | force_assert(NULL != b); |
if (b->used == 0) | force_assert(NULL != s || s_len == 0); |
b->used++; | |
|
|
memcpy(b->ptr + b->used - 1, s, s_len); | target_buf = buffer_string_prepare_append(b, s_len); |
b->used += s_len; | |
b->ptr[b->used - 1] = '\0'; | |
|
|
return 0; | if (0 == s_len) return; /* nothing to append */ |
} | |
|
|
int buffer_append_string_buffer(buffer *b, const buffer *src) { | memcpy(target_buf, s, s_len); |
if (!src) return -1; | |
if (src->used == 0) return 0; | |
|
|
return buffer_append_string_len(b, src->ptr, src->used - 1); | buffer_commit(b, s_len); |
} |
} |
|
|
int buffer_append_memory(buffer *b, const char *s, size_t s_len) { | void buffer_append_string_buffer(buffer *b, const buffer *src) { |
if (!s || !b) return -1; | if (NULL == src) { |
if (s_len == 0) return 0; | buffer_append_string_len(b, NULL, 0); |
| } else { |
buffer_prepare_append(b, s_len); | buffer_append_string_len(b, src->ptr, buffer_string_length(src)); |
memcpy(b->ptr + b->used, s, s_len); | } |
b->used += s_len; | |
| |
return 0; | |
} |
} |
|
|
int buffer_copy_memory(buffer *b, const char *s, size_t s_len) { | void buffer_append_uint_hex(buffer *b, uintmax_t value) { |
if (!s || !b) return -1; | |
| |
b->used = 0; | |
| |
return buffer_append_memory(b, s, s_len); | |
} | |
| |
int buffer_append_long_hex(buffer *b, unsigned long value) { | |
char *buf; |
char *buf; |
int shift = 0; |
int shift = 0; |
unsigned long copy = value; |
|
|
|
while (copy) { | { |
copy >>= 4; | uintmax_t copy = value; |
shift++; | do { |
| copy >>= 8; |
| shift += 2; /* counting nibbles (4 bits) */ |
| } while (0 != copy); |
} |
} |
if (shift == 0) |
|
shift++; |
|
if (shift & 0x01) |
|
shift++; |
|
|
|
buffer_prepare_append(b, shift + 1); | buf = buffer_string_prepare_append(b, shift); |
if (b->used == 0) | buffer_commit(b, shift); /* will fill below */ |
b->used++; | |
buf = b->ptr + (b->used - 1); | |
b->used += shift; | |
|
|
shift <<= 2; | shift <<= 2; /* count bits now */ |
while (shift > 0) { |
while (shift > 0) { |
shift -= 4; |
shift -= 4; |
*(buf++) = hex_chars[(value >> shift) & 0x0F]; |
*(buf++) = hex_chars[(value >> shift) & 0x0F]; |
} |
} |
*buf = '\0'; | } |
|
|
return 0; | static char* utostr(char * const buf_end, uintmax_t val) { |
| char *cur = buf_end; |
| do { |
| int mod = val % 10; |
| val /= 10; |
| /* prepend digit mod */ |
| *(--cur) = (char) ('0' + mod); |
| } while (0 != val); |
| return cur; |
} |
} |
|
|
int LI_ltostr(char *buf, long val) { | static char* itostr(char * const buf_end, intmax_t val) { |
char swap; | /* absolute value not defined for INTMAX_MIN, but can take absolute |
char *end; | * value of any negative number via twos complement cast to unsigned. |
int len = 1; | * negative sign is prepended after (now unsigned) value is converted |
| * to string */ |
| uintmax_t uval = val >= 0 ? (uintmax_t)val : ((uintmax_t)~val) + 1; |
| char *cur = utostr(buf_end, uval); |
| if (val < 0) *(--cur) = '-'; |
|
|
if (val < 0) { | return cur; |
len++; | } |
*(buf++) = '-'; | |
val = -val; | |
} | |
|
|
end = buf; | void buffer_append_int(buffer *b, intmax_t val) { |
while (val > 9) { | char buf[LI_ITOSTRING_LENGTH]; |
*(end++) = '0' + (val % 10); | char* const buf_end = buf + sizeof(buf); |
val = val / 10; | char *str; |
} | |
*(end) = '0' + val; | |
*(end + 1) = '\0'; | |
len += end - buf; | |
|
|
while (buf < end) { | force_assert(NULL != b); |
swap = *end; | |
*end = *buf; | |
*buf = swap; | |
|
|
buf++; | str = itostr(buf_end, val); |
end--; | force_assert(buf_end > str && str >= buf); |
} | |
|
|
return len; | buffer_append_string_len(b, str, buf_end - str); |
} |
} |
|
|
int buffer_append_long(buffer *b, long val) { | void buffer_copy_int(buffer *b, intmax_t val) { |
if (!b) return -1; | force_assert(NULL != b); |
|
|
buffer_prepare_append(b, 32); |
|
if (b->used == 0) |
|
b->used++; |
|
|
|
b->used += LI_ltostr(b->ptr + (b->used - 1), val); |
|
return 0; |
|
} |
|
|
|
int buffer_copy_long(buffer *b, long val) { |
|
if (!b) return -1; |
|
|
|
b->used = 0; |
b->used = 0; |
return buffer_append_long(b, val); | buffer_append_int(b, val); |
} |
} |
|
|
#if !defined(SIZEOF_LONG) || (SIZEOF_LONG != SIZEOF_OFF_T) | void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm) { |
int buffer_append_off_t(buffer *b, off_t val) { | size_t r; |
char swap; | char* buf; |
char *end; | force_assert(NULL != b); |
char *start; | force_assert(NULL != tm); |
int len = 1; | |
|
|
if (!b) return -1; | if (NULL == format || '\0' == format[0]) { |
| /* empty format */ |
| buffer_string_prepare_append(b, 0); |
| return; |
| } |
|
|
buffer_prepare_append(b, 32); | buf = buffer_string_prepare_append(b, 255); |
if (b->used == 0) | r = strftime(buf, buffer_string_space(b), format, tm); |
b->used++; | |
|
|
start = b->ptr + (b->used - 1); | /* 0 (in some apis buffer_string_space(b)) signals the string may have |
if (val < 0) { | * been too small; but the format could also just have lead to an empty |
len++; | * string |
*(start++) = '-'; | */ |
val = -val; | if (0 == r || r >= buffer_string_space(b)) { |
| /* give it a second try with a larger string */ |
| buf = buffer_string_prepare_append(b, 4095); |
| r = strftime(buf, buffer_string_space(b), format, tm); |
} |
} |
|
|
end = start; | if (r >= buffer_string_space(b)) r = 0; |
while (val > 9) { | |
*(end++) = '0' + (val % 10); | |
val = val / 10; | |
} | |
*(end) = '0' + val; | |
*(end + 1) = '\0'; | |
len += end - start; | |
|
|
while (start < end) { | buffer_commit(b, r); |
swap = *end; | } |
*end = *start; | |
*start = swap; | |
|
|
start++; |
|
end--; |
|
} |
|
|
|
b->used += len; | void li_itostrn(char *buf, size_t buf_len, intmax_t val) { |
return 0; | char p_buf[LI_ITOSTRING_LENGTH]; |
| char* const p_buf_end = p_buf + sizeof(p_buf); |
| char* str = p_buf_end - 1; |
| *str = '\0'; |
| |
| str = itostr(str, val); |
| force_assert(p_buf_end > str && str >= p_buf); |
| |
| force_assert(buf_len >= (size_t) (p_buf_end - str)); |
| memcpy(buf, str, p_buf_end - str); |
} |
} |
|
|
int buffer_copy_off_t(buffer *b, off_t val) { | void li_utostrn(char *buf, size_t buf_len, uintmax_t val) { |
if (!b) return -1; | char p_buf[LI_ITOSTRING_LENGTH]; |
| char* const p_buf_end = p_buf + sizeof(p_buf); |
| char* str = p_buf_end - 1; |
| *str = '\0'; |
|
|
b->used = 0; | str = utostr(str, val); |
return buffer_append_off_t(b, val); | force_assert(p_buf_end > str && str >= p_buf); |
| |
| force_assert(buf_len >= (size_t) (p_buf_end - str)); |
| memcpy(buf, str, p_buf_end - str); |
} |
} |
#endif /* !defined(SIZEOF_LONG) || (SIZEOF_LONG != SIZEOF_OFF_T) */ |
|
|
|
char int2hex(char c) { |
char int2hex(char c) { |
return hex_chars[(c & 0x0F)]; |
return hex_chars[(c & 0x0F)]; |
Line 397 char int2hex(char c) {
|
Line 366 char int2hex(char c) {
|
* returns 0xFF on invalid input. |
* returns 0xFF on invalid input. |
*/ |
*/ |
char hex2int(unsigned char hex) { |
char hex2int(unsigned char hex) { |
hex = hex - '0'; | unsigned char value = hex - '0'; |
if (hex > 9) { | if (value > 9) { |
hex = (hex + '0' - 1) | 0x20; | hex |= 0x20; /* to lower case */ |
hex = hex - 'a' + 11; | value = hex - 'a' + 10; |
| if (value < 10) value = 0xff; |
} |
} |
if (hex > 15) | if (value > 15) value = 0xff; |
hex = 0xFF; | |
|
|
return hex; | return value; |
} |
} |
|
|
|
|
/** |
|
* init the buffer |
|
* |
|
*/ |
|
|
|
buffer_array* buffer_array_init(void) { |
|
buffer_array *b; |
|
|
|
b = malloc(sizeof(*b)); |
|
|
|
assert(b); |
|
b->ptr = NULL; |
|
b->size = 0; |
|
b->used = 0; |
|
|
|
return b; |
|
} |
|
|
|
void buffer_array_reset(buffer_array *b) { |
|
size_t i; |
|
|
|
if (!b) return; |
|
|
|
/* if they are too large, reduce them */ |
|
for (i = 0; i < b->used; i++) { |
|
buffer_reset(b->ptr[i]); |
|
} |
|
|
|
b->used = 0; |
|
} |
|
|
|
|
|
/** |
|
* free the buffer_array |
|
* |
|
*/ |
|
|
|
void buffer_array_free(buffer_array *b) { |
|
size_t i; |
|
if (!b) return; |
|
|
|
for (i = 0; i < b->size; i++) { |
|
if (b->ptr[i]) buffer_free(b->ptr[i]); |
|
} |
|
free(b->ptr); |
|
free(b); |
|
} |
|
|
|
buffer *buffer_array_append_get_buffer(buffer_array *b) { |
|
size_t i; |
|
|
|
if (b->size == 0) { |
|
b->size = 16; |
|
b->ptr = malloc(sizeof(*b->ptr) * b->size); |
|
assert(b->ptr); |
|
for (i = 0; i < b->size; i++) { |
|
b->ptr[i] = NULL; |
|
} |
|
} else if (b->size == b->used) { |
|
b->size += 16; |
|
b->ptr = realloc(b->ptr, sizeof(*b->ptr) * b->size); |
|
assert(b->ptr); |
|
for (i = b->used; i < b->size; i++) { |
|
b->ptr[i] = NULL; |
|
} |
|
} |
|
|
|
if (b->ptr[b->used] == NULL) { |
|
b->ptr[b->used] = buffer_init(); |
|
} |
|
|
|
b->ptr[b->used]->used = 0; |
|
|
|
return b->ptr[b->used++]; |
|
} |
|
|
|
|
|
char * buffer_search_string_len(buffer *b, const char *needle, size_t len) { |
char * buffer_search_string_len(buffer *b, const char *needle, size_t len) { |
size_t i; |
size_t i; |
if (len == 0) return NULL; | force_assert(NULL != b); |
if (needle == NULL) return NULL; | force_assert(0 != len && NULL != needle); /* empty needles not allowed */ |
|
|
if (b->used < len) return NULL; |
if (b->used < len) return NULL; |
|
|
Line 502 char * buffer_search_string_len(buffer *b, const char
|
Line 393 char * buffer_search_string_len(buffer *b, const char
|
return NULL; |
return NULL; |
} |
} |
|
|
buffer *buffer_init_string(const char *str) { | int buffer_is_empty(const buffer *b) { |
buffer *b = buffer_init(); | return NULL == b || 0 == b->used; |
| |
buffer_copy_string(b, str); | |
| |
return b; | |
} |
} |
|
|
int buffer_is_empty(buffer *b) { | int buffer_string_is_empty(const buffer *b) { |
if (!b) return 1; | return 0 == buffer_string_length(b); |
return (b->used == 0); | |
} |
} |
|
|
/** |
/** |
Line 522 int buffer_is_empty(buffer *b) {
|
Line 408 int buffer_is_empty(buffer *b) {
|
* alignment properly. |
* alignment properly. |
*/ |
*/ |
|
|
int buffer_is_equal(buffer *a, buffer *b) { | int buffer_is_equal(const buffer *a, const buffer *b) { |
| force_assert(NULL != a && NULL != b); |
| |
if (a->used != b->used) return 0; |
if (a->used != b->used) return 0; |
if (a->used == 0) return 1; |
if (a->used == 0) return 1; |
|
|
return (0 == strcmp(a->ptr, b->ptr)); | return (0 == memcmp(a->ptr, b->ptr, a->used)); |
} |
} |
|
|
int buffer_is_equal_string(buffer *a, const char *s, size_t b_len) { | int buffer_is_equal_string(const buffer *a, const char *s, size_t b_len) { |
buffer b; | force_assert(NULL != a && NULL != s); |
| force_assert(b_len + 1 > b_len); |
|
|
b.ptr = (char *)s; | if (a->used != b_len + 1) return 0; |
b.used = b_len + 1; | if (0 != memcmp(a->ptr, s, b_len)) return 0; |
| if ('\0' != a->ptr[a->used-1]) return 0; |
|
|
return buffer_is_equal(a, &b); | return 1; |
} |
} |
|
|
/* buffer_is_equal_caseless_string(b, CONST_STR_LEN("value")) */ |
/* buffer_is_equal_caseless_string(b, CONST_STR_LEN("value")) */ |
int buffer_is_equal_caseless_string(buffer *a, const char *s, size_t b_len) { | int buffer_is_equal_caseless_string(const buffer *a, const char *s, size_t b_len) { |
| force_assert(NULL != a); |
if (a->used != b_len + 1) return 0; |
if (a->used != b_len + 1) return 0; |
|
force_assert('\0' == a->ptr[a->used - 1]); |
|
|
return (0 == strcasecmp(a->ptr, s)); |
return (0 == strcasecmp(a->ptr, s)); |
} |
} |
Line 554 int buffer_caseless_compare(const char *a, size_t a_le
|
Line 446 int buffer_caseless_compare(const char *a, size_t a_le
|
if (ca == cb) continue; |
if (ca == cb) continue; |
|
|
/* always lowercase for transitive results */ |
/* always lowercase for transitive results */ |
#if 1 |
|
if (ca >= 'A' && ca <= 'Z') ca |= 32; |
if (ca >= 'A' && ca <= 'Z') ca |= 32; |
if (cb >= 'A' && cb <= 'Z') cb |= 32; |
if (cb >= 'A' && cb <= 'Z') cb |= 32; |
#else |
|
/* try to produce code without branching (jumps) */ |
|
ca |= ((unsigned char)(ca - (unsigned char)'A') <= (unsigned char)('Z' - 'A')) ? 32 : 0; |
|
cb |= ((unsigned char)(cb - (unsigned char)'A') <= (unsigned char)('Z' - 'A')) ? 32 : 0; |
|
#endif |
|
|
|
if (ca == cb) continue; |
if (ca == cb) continue; |
return ca - cb; | return ((int)ca) - ((int)cb); |
} |
} |
if (a_len == b_len) return 0; |
if (a_len == b_len) return 0; |
return a_len - b_len; | return a_len < b_len ? -1 : 1; |
} |
} |
|
|
/** | int buffer_is_equal_right_len(const buffer *b1, const buffer *b2, size_t len) { |
* check if the rightmost bytes of the string are equal. | /* no len -> equal */ |
* | |
* | |
*/ | |
| |
int buffer_is_equal_right_len(buffer *b1, buffer *b2, size_t len) { | |
/* no, len -> equal */ | |
if (len == 0) return 1; |
if (len == 0) return 1; |
|
|
/* len > 0, but empty buffers -> not equal */ |
/* len > 0, but empty buffers -> not equal */ |
if (b1->used == 0 || b2->used == 0) return 0; |
if (b1->used == 0 || b2->used == 0) return 0; |
|
|
/* buffers too small -> not equal */ |
/* buffers too small -> not equal */ |
if (b1->used - 1 < len || b1->used - 1 < len) return 0; | if (b1->used - 1 < len || b2->used - 1 < len) return 0; |
|
|
if (0 == strncmp(b1->ptr + b1->used - 1 - len, | return 0 == memcmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len); |
b2->ptr + b2->used - 1 - len, len)) { | |
return 1; | |
} | |
| |
return 0; | |
} |
} |
|
|
int buffer_copy_string_hex(buffer *b, const char *in, size_t in_len) { | void li_tohex(char *buf, size_t buf_len, const char *s, size_t s_len) { |
size_t i; |
size_t i; |
|
force_assert(2 * s_len > s_len); |
|
force_assert(2 * s_len < buf_len); |
|
|
/* BO protection */ | for (i = 0; i < s_len; i++) { |
if (in_len * 2 < in_len) return -1; | buf[2*i] = hex_chars[(s[i] >> 4) & 0x0F]; |
| buf[2*i+1] = hex_chars[s[i] & 0x0F]; |
buffer_prepare_copy(b, in_len * 2 + 1); | |
| |
for (i = 0; i < in_len; i++) { | |
b->ptr[b->used++] = hex_chars[(in[i] >> 4) & 0x0F]; | |
b->ptr[b->used++] = hex_chars[in[i] & 0x0F]; | |
} |
} |
b->ptr[b->used++] = '\0'; | buf[2*s_len] = '\0'; |
| } |
|
|
return 0; | void buffer_copy_string_hex(buffer *b, const char *in, size_t in_len) { |
| /* overflow protection */ |
| force_assert(in_len * 2 > in_len); |
| |
| buffer_string_set_length(b, 2 * in_len); |
| li_tohex(b->ptr, buffer_string_length(b)+1, in, in_len); |
} |
} |
|
|
/* everything except: ! ( ) * - . 0-9 A-Z _ a-z */ |
/* everything except: ! ( ) * - . 0-9 A-Z _ a-z */ |
Line 623 static const char encoded_chars_rel_uri_part[] = {
|
Line 501 static const char encoded_chars_rel_uri_part[] = {
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, /* 70 - 7F { | } ~ DEL */ | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ |
Line 646 static const char encoded_chars_rel_uri[] = {
|
Line 524 static const char encoded_chars_rel_uri[] = {
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, /* 70 - 7F { | } ~ DEL */ | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ |
Line 663 static const char encoded_chars_html[] = {
|
Line 541 static const char encoded_chars_html[] = {
|
*/ |
*/ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ |
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ | 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F " & ' */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ |
Line 685 static const char encoded_chars_minimal_xml[] = {
|
Line 563 static const char encoded_chars_minimal_xml[] = {
|
*/ |
*/ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ |
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ | 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F " & ' */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ |
Line 747 static const char encoded_chars_http_header[] = {
|
Line 625 static const char encoded_chars_http_header[] = {
|
|
|
|
|
|
|
int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) { | void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) { |
unsigned char *ds, *d; |
unsigned char *ds, *d; |
size_t d_len, ndx; |
size_t d_len, ndx; |
const char *map = NULL; |
const char *map = NULL; |
|
|
if (!s || !b) return -1; | force_assert(NULL != b); |
| force_assert(NULL != s || 0 == s_len); |
|
|
if (b->ptr[b->used - 1] != '\0') { | if (0 == s_len) return; |
SEGFAULT(); | |
} | |
|
|
if (s_len == 0) return 0; |
|
|
|
switch(encoding) { |
switch(encoding) { |
case ENCODING_REL_URI: |
case ENCODING_REL_URI: |
map = encoded_chars_rel_uri; |
map = encoded_chars_rel_uri; |
Line 779 int buffer_append_string_encoded(buffer *b, const char
|
Line 654 int buffer_append_string_encoded(buffer *b, const char
|
case ENCODING_HTTP_HEADER: |
case ENCODING_HTTP_HEADER: |
map = encoded_chars_http_header; |
map = encoded_chars_http_header; |
break; |
break; |
case ENCODING_UNSET: |
|
break; |
|
} |
} |
|
|
assert(map != NULL); | force_assert(NULL != map); |
|
|
/* count to-be-encoded-characters */ |
/* count to-be-encoded-characters */ |
for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { |
for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { |
Line 801 int buffer_append_string_encoded(buffer *b, const char
|
Line 674 int buffer_append_string_encoded(buffer *b, const char
|
case ENCODING_HEX: |
case ENCODING_HEX: |
d_len += 2; |
d_len += 2; |
break; |
break; |
case ENCODING_UNSET: |
|
break; |
|
} |
} |
} else { |
} else { |
d_len ++; | d_len++; |
} |
} |
} |
} |
|
|
buffer_prepare_append(b, d_len); | d = (unsigned char*) buffer_string_prepare_append(b, d_len); |
| buffer_commit(b, d_len); /* fill below */ |
| force_assert('\0' == *d); |
|
|
for (ds = (unsigned char *)s, d = (unsigned char *)b->ptr + b->used - 1, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { | for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { |
if (map[*ds]) { |
if (map[*ds]) { |
switch(encoding) { |
switch(encoding) { |
case ENCODING_REL_URI: |
case ENCODING_REL_URI: |
Line 837 int buffer_append_string_encoded(buffer *b, const char
|
Line 710 int buffer_append_string_encoded(buffer *b, const char
|
d[d_len++] = *ds; |
d[d_len++] = *ds; |
d[d_len++] = '\t'; |
d[d_len++] = '\t'; |
break; |
break; |
case ENCODING_UNSET: |
|
break; |
|
} |
} |
} else { |
} else { |
d[d_len++] = *ds; |
d[d_len++] = *ds; |
} |
} |
} |
} |
|
} |
|
|
/* terminate buffer and calculate new length */ | void buffer_append_string_c_escaped(buffer *b, const char *s, size_t s_len) { |
b->ptr[b->used + d_len - 1] = '\0'; | unsigned char *ds, *d; |
| size_t d_len, ndx; |
|
|
b->used += d_len; | force_assert(NULL != b); |
| force_assert(NULL != s || 0 == s_len); |
|
|
return 0; | if (0 == s_len) return; |
| |
| /* count to-be-encoded-characters */ |
| for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { |
| if ((*ds < 0x20) /* control character */ |
| || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ |
| switch (*ds) { |
| case '\t': |
| case '\r': |
| case '\n': |
| d_len += 2; |
| break; |
| default: |
| d_len += 4; /* \xCC */ |
| break; |
| } |
| } else { |
| d_len++; |
| } |
| } |
| |
| d = (unsigned char*) buffer_string_prepare_append(b, d_len); |
| buffer_commit(b, d_len); /* fill below */ |
| force_assert('\0' == *d); |
| |
| for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { |
| if ((*ds < 0x20) /* control character */ |
| || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ |
| d[d_len++] = '\\'; |
| switch (*ds) { |
| case '\t': |
| d[d_len++] = 't'; |
| break; |
| case '\r': |
| d[d_len++] = 'r'; |
| break; |
| case '\n': |
| d[d_len++] = 'n'; |
| break; |
| default: |
| d[d_len++] = 'x'; |
| d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; |
| d[d_len++] = hex_chars[(*ds) & 0x0F]; |
| break; |
| } |
| } else { |
| d[d_len++] = *ds; |
| } |
| } |
} |
} |
|
|
|
|
|
void buffer_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header) { |
|
size_t i, j; |
|
|
|
force_assert(NULL != b); |
|
force_assert(NULL != s || 0 == s_len); |
|
|
|
buffer_reset(b); |
|
|
|
if (is_http_header && NULL != s && 0 != strcasecmp(s, "CONTENT-TYPE")) { |
|
buffer_string_prepare_append(b, s_len + 5); |
|
buffer_copy_string_len(b, CONST_STR_LEN("HTTP_")); |
|
} else { |
|
buffer_string_prepare_append(b, s_len); |
|
} |
|
|
|
j = buffer_string_length(b); |
|
for (i = 0; i < s_len; ++i) { |
|
unsigned char cr = s[i]; |
|
if (light_isalpha(cr)) { |
|
/* upper-case */ |
|
cr &= ~32; |
|
} else if (!light_isdigit(cr)) { |
|
cr = '_'; |
|
} |
|
b->ptr[j++] = cr; |
|
} |
|
b->used = j; |
|
b->ptr[b->used++] = '\0'; |
|
} |
|
|
/* decodes url-special-chars inplace. |
/* decodes url-special-chars inplace. |
* replaces non-printable characters with '_' |
* replaces non-printable characters with '_' |
*/ |
*/ |
|
|
static int buffer_urldecode_internal(buffer *url, int is_query) { | static void buffer_urldecode_internal(buffer *url, int is_query) { |
unsigned char high, low; |
unsigned char high, low; |
const char *src; | char *src; |
char *dst; |
char *dst; |
|
|
if (!url || !url->ptr) return -1; | force_assert(NULL != url); |
| if (buffer_string_is_empty(url)) return; |
|
|
src = (const char*) url->ptr; | force_assert('\0' == url->ptr[url->used-1]); |
dst = (char*) url->ptr; | |
|
|
while ((*src) != '\0') { | src = (char*) url->ptr; |
| |
| while ('\0' != *src) { |
| if ('%' == *src) break; |
| if (is_query && '+' == *src) *src = ' '; |
| src++; |
| } |
| dst = src; |
| |
| while ('\0' != *src) { |
if (is_query && *src == '+') { |
if (is_query && *src == '+') { |
*dst = ' '; |
*dst = ' '; |
} else if (*src == '%') { |
} else if (*src == '%') { |
*dst = '%'; |
*dst = '%'; |
|
|
high = hex2int(*(src + 1)); |
high = hex2int(*(src + 1)); |
if (high != 0xFF) { | if (0xFF != high) { |
low = hex2int(*(src + 2)); |
low = hex2int(*(src + 2)); |
if (low != 0xFF) { | if (0xFF != low) { |
high = (high << 4) | low; |
high = (high << 4) | low; |
|
|
/* map control-characters out */ |
/* map control-characters out */ |
Line 897 static int buffer_urldecode_internal(buffer *url, int
|
Line 858 static int buffer_urldecode_internal(buffer *url, int
|
|
|
*dst = '\0'; |
*dst = '\0'; |
url->used = (dst - url->ptr) + 1; |
url->used = (dst - url->ptr) + 1; |
|
|
return 0; |
|
} |
} |
|
|
int buffer_urldecode_path(buffer *url) { | void buffer_urldecode_path(buffer *url) { |
return buffer_urldecode_internal(url, 0); | buffer_urldecode_internal(url, 0); |
} |
} |
|
|
int buffer_urldecode_query(buffer *url) { | void buffer_urldecode_query(buffer *url) { |
return buffer_urldecode_internal(url, 1); | buffer_urldecode_internal(url, 1); |
} |
} |
|
|
/* Remove "/../", "//", "/./" parts from path. | /* - special case: empty string returns empty string |
| * - on windows or cygwin: replace \ with / |
| * - strip leading spaces |
| * - prepends "/" if not present already |
| * - resolve "/../", "//" and "/./" the usual way: |
| * the first one removes a preceding component, the other two |
| * get compressed to "/". |
| * - "/." and "/.." at the end are similar, but always leave a trailing |
| * "/" |
* |
* |
* /blah/.. gets / |
* /blah/.. gets / |
* /blah/../foo gets /foo |
* /blah/../foo gets /foo |
Line 920 int buffer_urldecode_query(buffer *url) {
|
Line 887 int buffer_urldecode_query(buffer *url) {
|
* the operation is performed in-place. |
* the operation is performed in-place. |
*/ |
*/ |
|
|
int buffer_path_simplify(buffer *dest, buffer *src) | void buffer_path_simplify(buffer *dest, buffer *src) |
{ |
{ |
int toklen; | /* current character, the one before, and the one before that from input */ |
char c, pre1; | char c, pre1, pre2; |
char *start, *slash, *walk, *out; |
char *start, *slash, *walk, *out; |
unsigned short pre; |
|
|
|
if (src == NULL || src->ptr == NULL || dest == NULL) | force_assert(NULL != dest && NULL != src); |
return -1; | |
|
|
if (src == dest) | if (buffer_string_is_empty(src)) { |
buffer_prepare_append(dest, 1); | buffer_string_prepare_copy(dest, 0); |
else | return; |
buffer_prepare_copy(dest, src->used + 1); | } |
|
|
walk = src->ptr; | force_assert('\0' == src->ptr[src->used-1]); |
start = dest->ptr; | |
out = dest->ptr; | |
slash = dest->ptr; | |
|
|
|
/* might need one character more for the '/' prefix */ |
|
if (src == dest) { |
|
buffer_string_prepare_append(dest, 1); |
|
} else { |
|
buffer_string_prepare_copy(dest, buffer_string_length(src) + 1); |
|
} |
|
|
#if defined(__WIN32) || defined(__CYGWIN__) |
#if defined(__WIN32) || defined(__CYGWIN__) |
/* cygwin is treating \ and / the same, so we have to that too | /* cygwin is treating \ and / the same, so we have to that too */ |
*/ | { |
| char *p; |
for (walk = src->ptr; *walk; walk++) { | for (p = src->ptr; *p; p++) { |
if (*walk == '\\') *walk = '/'; | if (*p == '\\') *p = '/'; |
| } |
} |
} |
walk = src->ptr; |
|
#endif |
#endif |
|
|
|
walk = src->ptr; |
|
start = dest->ptr; |
|
out = dest->ptr; |
|
slash = dest->ptr; |
|
|
|
/* skip leading spaces */ |
while (*walk == ' ') { |
while (*walk == ' ') { |
walk++; |
walk++; |
} |
} |
|
|
pre1 = *(walk++); | pre2 = pre1 = 0; |
c = *(walk++); | c = *(walk++); |
pre = pre1; | /* prefix with '/' if not already present */ |
if (pre1 != '/') { | if (c != '/') { |
pre = ('/' << 8) | pre1; | pre1 = '/'; |
*(out++) = '/'; |
*(out++) = '/'; |
} |
} |
*(out++) = pre1; |
|
|
|
if (pre1 == '\0') { | while (c != '\0') { |
dest->used = (out - start) + 1; | /* assert((src != dest || out <= walk) && slash <= out); */ |
return 0; | /* the following comments about out and walk are only interesting if |
} | * src == dest; otherwise the memory areas don't overlap anyway. |
| */ |
| pre2 = pre1; |
| pre1 = c; |
|
|
while (1) { | /* possibly: out == walk - need to read first */ |
| c = *walk; |
| *out = pre1; |
| |
| out++; |
| walk++; |
| /* (out <= walk) still true; also now (slash < out) */ |
| |
if (c == '/' || c == '\0') { |
if (c == '/' || c == '\0') { |
toklen = out - slash; | const size_t toklen = out - slash; |
if (toklen == 3 && pre == (('.' << 8) | '.')) { | if (toklen == 3 && pre2 == '.' && pre1 == '.') { |
| /* "/../" or ("/.." at end of string) */ |
out = slash; |
out = slash; |
|
/* if there is something before "/..", there is at least one |
|
* component, which needs to be removed */ |
if (out > start) { |
if (out > start) { |
out--; |
out--; |
while (out > start && *out != '/') { | while (out > start && *out != '/') out--; |
out--; | |
} | |
} |
} |
|
|
if (c == '\0') | /* don't kill trailing '/' at end of path */ |
out++; | if (c == '\0') out++; |
} else if (toklen == 1 || pre == (('/' << 8) | '.')) { | /* slash < out before, so out_new <= slash + 1 <= out_before <= walk */ |
| } else if (toklen == 1 || (pre2 == '/' && pre1 == '.')) { |
| /* "//" or "/./" or (("/" or "/.") at end of string) */ |
out = slash; |
out = slash; |
if (c == '\0') | /* don't kill trailing '/' at end of path */ |
out++; | if (c == '\0') out++; |
| /* slash < out before, so out_new <= slash + 1 <= out_before <= walk */ |
} |
} |
|
|
slash = out; |
slash = out; |
} |
} |
|
|
if (c == '\0') |
|
break; |
|
|
|
pre1 = c; |
|
pre = (pre << 8) | pre1; |
|
c = *walk; |
|
*out = pre1; |
|
|
|
out++; |
|
walk++; |
|
} |
} |
|
|
*out = '\0'; | buffer_string_set_length(dest, out - start); |
dest->used = (out - start) + 1; | |
| |
return 0; | |
} |
} |
|
|
int light_isdigit(int c) { |
int light_isdigit(int c) { |
Line 1030 int light_isalnum(int c) {
|
Line 1003 int light_isalnum(int c) {
|
return light_isdigit(c) || light_isalpha(c); |
return light_isdigit(c) || light_isalpha(c); |
} |
} |
|
|
int buffer_to_lower(buffer *b) { | void buffer_to_lower(buffer *b) { |
char *c; | size_t i; |
|
|
if (b->used == 0) return 0; | for (i = 0; i < b->used; ++i) { |
| char c = b->ptr[i]; |
for (c = b->ptr; *c; c++) { | if (c >= 'A' && c <= 'Z') b->ptr[i] |= 0x20; |
if (*c >= 'A' && *c <= 'Z') { | |
*c |= 32; | |
} | |
} |
} |
|
} |
|
|
return 0; | |
| void buffer_to_upper(buffer *b) { |
| size_t i; |
| |
| for (i = 0; i < b->used; ++i) { |
| char c = b->ptr[i]; |
| if (c >= 'A' && c <= 'Z') b->ptr[i] &= ~0x20; |
| } |
} |
} |
|
|
|
#ifdef HAVE_LIBUNWIND |
|
# define UNW_LOCAL_ONLY |
|
# include <libunwind.h> |
|
|
int buffer_to_upper(buffer *b) { | void print_backtrace(FILE *file) { |
char *c; | unw_cursor_t cursor; |
| unw_context_t context; |
| int ret; |
| unsigned int frame = 0; |
|
|
if (b->used == 0) return 0; | if (0 != (ret = unw_getcontext(&context))) goto error; |
| if (0 != (ret = unw_init_local(&cursor, &context))) goto error; |
|
|
for (c = b->ptr; *c; c++) { | fprintf(file, "Backtrace:\n"); |
if (*c >= 'a' && *c <= 'z') { | |
*c &= ~32; | while (0 < (ret = unw_step(&cursor))) { |
| unw_word_t proc_ip = 0; |
| unw_proc_info_t procinfo; |
| char procname[256]; |
| unw_word_t proc_offset = 0; |
| |
| if (0 != (ret = unw_get_reg(&cursor, UNW_REG_IP, &proc_ip))) goto error; |
| |
| if (0 == proc_ip) { |
| /* without an IP the other functions are useless; unw_get_proc_name would return UNW_EUNSPEC */ |
| ++frame; |
| fprintf(file, "%u: (nil)\n", frame); |
| continue; |
} |
} |
|
|
|
if (0 != (ret = unw_get_proc_info(&cursor, &procinfo))) goto error; |
|
|
|
if (0 != (ret = unw_get_proc_name(&cursor, procname, sizeof(procname), &proc_offset))) { |
|
switch (-ret) { |
|
case UNW_ENOMEM: |
|
memset(procname + sizeof(procname) - 4, '.', 3); |
|
procname[sizeof(procname) - 1] = '\0'; |
|
break; |
|
case UNW_ENOINFO: |
|
procname[0] = '?'; |
|
procname[1] = '\0'; |
|
proc_offset = 0; |
|
break; |
|
default: |
|
snprintf(procname, sizeof(procname), "?? (unw_get_proc_name error %d)", -ret); |
|
break; |
|
} |
|
} |
|
|
|
++frame; |
|
fprintf(file, "%u: %s (+0x%x) [%p]\n", |
|
frame, |
|
procname, |
|
(unsigned int) proc_offset, |
|
(void*)(uintptr_t)proc_ip); |
} |
} |
|
|
return 0; | if (0 != ret) goto error; |
| |
| return; |
| |
| error: |
| fprintf(file, "Error while generating backtrace: unwind error %i\n", (int) -ret); |
| } |
| #else |
| void print_backtrace(FILE *file) { |
| UNUSED(file); |
| } |
| #endif |
| |
| void log_failed_assert(const char *filename, unsigned int line, const char *msg) { |
| /* can't use buffer here; could lead to recursive assertions */ |
| fprintf(stderr, "%s.%u: %s\n", filename, line, msg); |
| print_backtrace(stderr); |
| fflush(stderr); |
| abort(); |
} |
} |