1
0
mirror of https://github.com/InfrastructureServices/vsftpd.git synced 2025-04-19 01:24:02 +03:00
vsftpd/str.c
2021-04-08 16:34:35 +02:00

838 lines
18 KiB
C

/*
* Part of Very Secure FTPd
* Licence: GPL v2
* Author: Chris Evans
* str.c
*
* Generic string handling functions. The fact that a string is implemented
* internally using a buffer is not exposed in the API. If you can't see
* the buffers, you can't handle them in a screwed way. Or so goes the
* theory, anyway...
*/
/* Anti-lamer measures deployed, sir! */
#define PRIVATE_HANDS_OFF_p_buf p_buf
#define PRIVATE_HANDS_OFF_len len
#define PRIVATE_HANDS_OFF_alloc_bytes alloc_bytes
#include "str.h"
/* Ick. Its for die() */
#include "utility.h"
#include "sysutil.h"
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
/* File local functions */
static void str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
const char* p_text, int is_reverse);
static int str_equal_internal(const char* p_buf1, unsigned int buf1_len,
const char* p_buf2, unsigned int buf2_len);
/* Private functions */
static void
s_setbuf(struct mystr* p_str, char* p_newbuf)
{
if (p_str->p_buf != 0)
{
bug("p_buf not NULL when setting it");
}
p_str->p_buf = p_newbuf;
}
void
private_str_alloc_memchunk(struct mystr* p_str, const char* p_src,
unsigned int len)
{
/* Make sure this will fit in the buffer */
unsigned int buf_needed;
if (len + 1 < len)
{
bug("integer overflow");
}
buf_needed = len + 1;
if (buf_needed > p_str->alloc_bytes)
{
str_free(p_str);
s_setbuf(p_str, vsf_sysutil_malloc(buf_needed));
p_str->alloc_bytes = buf_needed;
}
vsf_sysutil_memcpy(p_str->p_buf, p_src, len);
p_str->p_buf[len] = '\0';
p_str->len = len;
}
void
private_str_append_memchunk(struct mystr* p_str, const char* p_src,
unsigned int len)
{
unsigned int buf_needed;
if (len + p_str->len < len)
{
bug("integer overflow");
}
buf_needed = len + p_str->len;
if (buf_needed + 1 < buf_needed)
{
bug("integer overflow");
}
buf_needed++;
if (buf_needed > p_str->alloc_bytes)
{
p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, buf_needed);
p_str->alloc_bytes = buf_needed;
}
vsf_sysutil_memcpy(p_str->p_buf + p_str->len, p_src, len);
p_str->p_buf[p_str->len + len] = '\0';
p_str->len += len;
}
/* Public functions */
void
str_alloc_text(struct mystr* p_str, const char* p_src)
{
unsigned int len = vsf_sysutil_strlen(p_src);
private_str_alloc_memchunk(p_str, p_src, len);
}
void
str_copy(struct mystr* p_dest, const struct mystr* p_src)
{
private_str_alloc_memchunk(p_dest, p_src->p_buf, p_src->len);
}
const char*
str_strdup(const struct mystr* p_str)
{
return vsf_sysutil_strdup(str_getbuf(p_str));
}
const char*
str_strdup_trimmed(const struct mystr* p_str)
{
const char* p_trimmed = str_getbuf(p_str);
int h, t, newlen;
for (h = 0; h < (int)str_getlen(p_str) && vsf_sysutil_isspace(p_trimmed[h]); h++) ;
for (t = str_getlen(p_str) - 1; t >= 0 && vsf_sysutil_isspace(p_trimmed[t]); t--) ;
newlen = t - h + 1;
return (newlen > 0) ? vsf_sysutil_strndup(p_trimmed+h, (unsigned int)newlen) : 0L;
}
void
str_alloc_alt_term(struct mystr* p_str, const char* p_src, char term)
{
const char* p_search = p_src;
unsigned int len = 0;
while (*p_search != term)
{
p_search++;
len++;
if (len == 0)
{
bug("integer overflow");
}
}
private_str_alloc_memchunk(p_str, p_src, len);
}
void
str_alloc_ulong(struct mystr* p_str, unsigned long the_long)
{
str_alloc_text(p_str, vsf_sysutil_ulong_to_str(the_long));
}
void
str_alloc_filesize_t(struct mystr* p_str, filesize_t the_filesize)
{
str_alloc_text(p_str, vsf_sysutil_filesize_t_to_str(the_filesize));
}
void
str_free(struct mystr* p_str)
{
if (p_str->p_buf != 0)
{
vsf_sysutil_free(p_str->p_buf);
}
p_str->p_buf = 0;
p_str->len = 0;
p_str->alloc_bytes = 0;
}
void
str_empty(struct mystr* p_str)
{
/* Ensure a buffer is allocated. */
(void) str_getbuf(p_str);
str_trunc(p_str, 0);
}
void
str_trunc(struct mystr* p_str, unsigned int trunc_len)
{
if (trunc_len >= p_str->alloc_bytes)
{
bug("trunc_len not smaller than alloc_bytes in str_trunc");
}
p_str->len = trunc_len;
p_str->p_buf[p_str->len] = '\0';
}
void
str_reserve(struct mystr* p_str, unsigned int res_len)
{
/* Reserve space for the trailing zero as well. */
res_len++;
if (res_len == 0)
{
bug("integer overflow");
}
if (res_len > p_str->alloc_bytes)
{
p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, res_len);
p_str->alloc_bytes = res_len;
}
p_str->p_buf[res_len - 1] = '\0';
}
int
str_isempty(const struct mystr* p_str)
{
return (p_str->len == 0);
}
unsigned int
str_getlen(const struct mystr* p_str)
{
return p_str->len;
}
const char*
str_getbuf(const struct mystr* p_str)
{
if (p_str->p_buf == 0)
{
if (p_str->len != 0 || p_str->alloc_bytes != 0)
{
bug("p_buf NULL and len or alloc_bytes != 0 in str_getbuf");
}
private_str_alloc_memchunk((struct mystr*)p_str, 0, 0);
}
return p_str->p_buf;
}
int
str_strcmp(const struct mystr* p_str1, const struct mystr* p_str2)
{
return str_equal_internal(p_str1->p_buf, p_str1->len,
p_str2->p_buf, p_str2->len);
}
static int
str_equal_internal(const char* p_buf1, unsigned int buf1_len,
const char* p_buf2, unsigned int buf2_len)
{
int retval;
unsigned int minlen = buf1_len;
if (buf2_len < minlen)
{
minlen = buf2_len;
}
retval = vsf_sysutil_memcmp(p_buf1, p_buf2, minlen);
if (retval != 0 || buf1_len == buf2_len)
{
return retval;
}
/* Strings equal but lengths differ. The greater one, then, is the longer */
return (int) (buf1_len - buf2_len);
}
int
str_equal(const struct mystr* p_str1, const struct mystr* p_str2)
{
return (str_strcmp(p_str1, p_str2) == 0);
}
int
str_equal_text(const struct mystr* p_str, const char* p_text)
{
unsigned int cmplen = vsf_sysutil_strlen(p_text);
return (str_equal_internal(p_str->p_buf, p_str->len, p_text, cmplen) == 0);
}
void
str_append_str(struct mystr* p_str, const struct mystr* p_other)
{
private_str_append_memchunk(p_str, p_other->p_buf, p_other->len);
}
void
str_append_text(struct mystr* p_str, const char* p_src)
{
unsigned int len = vsf_sysutil_strlen(p_src);
private_str_append_memchunk(p_str, p_src, len);
}
void
str_append_char(struct mystr* p_str, char the_char)
{
private_str_append_memchunk(p_str, &the_char, sizeof(the_char));
}
void
str_append_ulong(struct mystr* p_str, unsigned long the_ulong)
{
str_append_text(p_str, vsf_sysutil_ulong_to_str(the_ulong));
}
void
str_append_filesize_t(struct mystr* p_str, filesize_t the_filesize)
{
str_append_text(p_str, vsf_sysutil_filesize_t_to_str(the_filesize));
}
void
str_append_double(struct mystr* p_str, double the_double)
{
str_append_text(p_str, vsf_sysutil_double_to_str(the_double));
}
void
str_upper(struct mystr* p_str)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
p_str->p_buf[i] = (char) vsf_sysutil_toupper(p_str->p_buf[i]);
}
}
void
str_rpad(struct mystr* p_str, const unsigned int min_width)
{
unsigned int to_pad;
if (p_str->len >= min_width)
{
return;
}
to_pad = min_width - p_str->len;
while (to_pad--)
{
str_append_char(p_str, ' ');
}
}
void
str_lpad(struct mystr* p_str, const unsigned int min_width)
{
static struct mystr s_tmp_str;
unsigned int to_pad;
if (p_str->len >= min_width)
{
return;
}
to_pad = min_width - p_str->len;
str_empty(&s_tmp_str);
while (to_pad--)
{
str_append_char(&s_tmp_str, ' ');
}
str_append_str(&s_tmp_str, p_str);
str_copy(p_str, &s_tmp_str);
}
void
str_replace_char(struct mystr* p_str, char from, char to)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
if (p_str->p_buf[i] == from)
{
p_str->p_buf[i] = to;
}
}
}
void
str_replace_text(struct mystr* p_str, const char* p_from, const char* p_to)
{
static struct mystr s_lhs_chunk_str;
static struct mystr s_rhs_chunk_str;
unsigned int lhs_len;
str_copy(&s_lhs_chunk_str, p_str);
str_free(p_str);
do
{
lhs_len = str_getlen(&s_lhs_chunk_str);
str_split_text(&s_lhs_chunk_str, &s_rhs_chunk_str, p_from);
/* Copy lhs to destination */
str_append_str(p_str, &s_lhs_chunk_str);
/* If this was a 'hit', append the 'to' text */
if (str_getlen(&s_lhs_chunk_str) < lhs_len)
{
str_append_text(p_str, p_to);
}
/* Current rhs becomes new lhs */
str_copy(&s_lhs_chunk_str, &s_rhs_chunk_str);
} while (!str_isempty(&s_lhs_chunk_str));
}
void
str_split_char(struct mystr* p_src, struct mystr* p_rhs, char c)
{
/* Just use str_split_text */
char ministr[2];
ministr[0] = c;
ministr[1] = '\0';
str_split_text(p_src, p_rhs, ministr);
}
void
str_split_char_reverse(struct mystr* p_src, struct mystr* p_rhs, char c)
{
/* Just use str_split_text_reverse */
char ministr[2];
ministr[0] = c;
ministr[1] = '\0';
str_split_text_reverse(p_src, p_rhs, ministr);
}
void
str_split_text(struct mystr* p_src, struct mystr* p_rhs, const char* p_text)
{
str_split_text_common(p_src, p_rhs, p_text, 0);
}
void
str_split_text_reverse(struct mystr* p_src, struct mystr* p_rhs,
const char* p_text)
{
str_split_text_common(p_src, p_rhs, p_text, 1);
}
static void
str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
const char* p_text, int is_reverse)
{
struct str_locate_result locate_result;
unsigned int indexx;
unsigned int search_len = vsf_sysutil_strlen(p_text);
if (is_reverse)
{
locate_result = str_locate_text_reverse(p_src, p_text);
}
else
{
locate_result = str_locate_text(p_src, p_text);
}
/* Not found? */
if (!locate_result.found)
{
str_empty(p_rhs);
return;
}
indexx = locate_result.index;
if (indexx + search_len > p_src->len)
{
bug("indexx invalid in str_split_text");
}
/* Build rhs */
private_str_alloc_memchunk(p_rhs, p_src->p_buf + indexx + search_len,
p_src->len - indexx - search_len);
/* Build lhs */
str_trunc(p_src, indexx);
}
struct str_locate_result
str_locate_str(const struct mystr* p_str, const struct mystr* p_look_str)
{
return str_locate_text(p_str, str_getbuf(p_look_str));
}
struct str_locate_result
str_locate_str_reverse(const struct mystr* p_str,
const struct mystr* p_look_str)
{
return str_locate_text_reverse(p_str, str_getbuf(p_look_str));
}
struct str_locate_result
str_locate_char(const struct mystr* p_str, char look_char)
{
char look_str[2];
look_str[0] = look_char;
look_str[1] = '\0';
return str_locate_text(p_str, look_str);
}
struct str_locate_result
str_locate_chars(const struct mystr* p_str, const char* p_chars)
{
struct str_locate_result retval;
unsigned int num_chars = vsf_sysutil_strlen(p_chars);
unsigned int i = 0;
retval.found = 0;
retval.char_found = 0;
retval.index = 0;
for (; i < p_str->len; ++i)
{
unsigned int j = 0;
char this_char = p_str->p_buf[i];
for (; j < num_chars; ++j)
{
if (p_chars[j] == this_char)
{
retval.found = 1;
retval.index = i;
retval.char_found = p_chars[j];
return retval;
}
}
}
return retval;
}
struct str_locate_result
str_locate_text(const struct mystr* p_str, const char* p_text)
{
struct str_locate_result retval;
unsigned int i;
unsigned int text_len = vsf_sysutil_strlen(p_text);
retval.found = 0;
retval.char_found = 0;
retval.index = 0;
if (text_len == 0 || text_len > p_str->len)
{
/* Not found */
return retval;
}
for (i=0; i <= (p_str->len - text_len); i++)
{
if (vsf_sysutil_memcmp(p_str->p_buf + i, p_text, text_len) == 0)
{
retval.found = 1;
retval.index = i;
return retval;
}
}
/* Not found */
return retval;
}
struct str_locate_result
str_locate_text_reverse(const struct mystr* p_str, const char* p_text)
{
struct str_locate_result retval;
unsigned int i;
unsigned int text_len = vsf_sysutil_strlen(p_text);
retval.found = 0;
retval.char_found = 0;
retval.index = 0;
if (text_len == 0 || text_len > p_str->len)
{
return retval;
}
i = p_str->len - text_len;
/* Want to go through loop once even if i==0 */
while (1)
{
if (vsf_sysutil_memcmp(p_str->p_buf + i, p_text, text_len) == 0)
{
retval.found = 1;
retval.index = i;
return retval;
}
if (i == 0)
{
break;
}
i--;
}
/* Not found */
return retval;
}
void
str_left(const struct mystr* p_str, struct mystr* p_out, unsigned int chars)
{
if (chars > p_str->len)
{
bug("chars invalid in str_left");
}
private_str_alloc_memchunk(p_out, p_str->p_buf, chars);
}
void
str_right(const struct mystr* p_str, struct mystr* p_out, unsigned int chars)
{
unsigned int indexx = p_str->len - chars;
if (chars > p_str->len)
{
bug("chars invalid in str_right");
}
private_str_alloc_memchunk(p_out, p_str->p_buf + indexx, chars);
}
void
str_mid_to_end(const struct mystr* p_str, struct mystr* p_out,
unsigned int indexx)
{
if (indexx > p_str->len)
{
bug("invalid indexx in str_mid_to_end");
}
private_str_alloc_memchunk(p_out, p_str->p_buf + indexx,
p_str->len - indexx);
}
char
str_get_char_at(const struct mystr* p_str, const unsigned int indexx)
{
if (indexx >= p_str->len)
{
bug("bad indexx in str_get_char_at");
}
return p_str->p_buf[indexx];
}
int
str_contains_space(const struct mystr* p_str)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
if (vsf_sysutil_isspace(p_str->p_buf[i]))
{
return 1;
}
}
return 0;
}
int
str_all_space(const struct mystr* p_str)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
if (!vsf_sysutil_isspace(p_str->p_buf[i]))
{
return 0;
}
}
return 1;
}
int
str_contains_unprintable(const struct mystr* p_str)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
if (!vsf_sysutil_isprint(p_str->p_buf[i]))
{
return 1;
}
}
return 0;
}
int
str_atoi(const struct mystr* p_str)
{
return vsf_sysutil_atoi(str_getbuf(p_str));
}
filesize_t
str_a_to_filesize_t(const struct mystr* p_str)
{
return vsf_sysutil_a_to_filesize_t(str_getbuf(p_str));
}
unsigned int
str_octal_to_uint(const struct mystr* p_str)
{
return vsf_sysutil_octal_to_uint(str_getbuf(p_str));
}
int
str_getline(const struct mystr* p_str, struct mystr* p_line_str,
unsigned int* p_pos)
{
unsigned int start_pos = *p_pos;
unsigned int curr_pos = start_pos;
unsigned int buf_len = str_getlen(p_str);
const char* p_buf = str_getbuf(p_str);
unsigned int out_len;
if (start_pos > buf_len)
{
bug("p_pos out of range in str_getline");
}
str_empty(p_line_str);
if (start_pos == buf_len)
{
return 0;
}
while (curr_pos < buf_len && p_buf[curr_pos] != '\n')
{
curr_pos++;
if (curr_pos == 0)
{
bug("integer overflow");
}
}
out_len = curr_pos - start_pos;
/* If we ended on a \n - skip it */
if (curr_pos < buf_len && p_buf[curr_pos] == '\n')
{
curr_pos++;
if (curr_pos == 0)
{
bug("integer overflow");
}
}
private_str_alloc_memchunk(p_line_str, p_buf + start_pos, out_len);
*p_pos = curr_pos;
return 1;
}
int
str_contains_line(const struct mystr* p_str, const struct mystr* p_line_str)
{
static struct mystr s_curr_line_str;
unsigned int pos = 0;
while (str_getline(p_str, &s_curr_line_str, &pos))
{
if (str_equal(&s_curr_line_str, p_line_str))
{
return 1;
}
}
return 0;
}
void
str_replace_unprintable(struct mystr* p_str, char new_char)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
if (!vsf_sysutil_isprint(p_str->p_buf[i]))
{
p_str->p_buf[i] = new_char;
}
}
}
void
str_replace_unprintable_with_hex(struct mystr* p_str)
{
unsigned int ups_size = sizeof(unsigned int) * (p_str->len);
if (ups_size < p_str->len)
{
str_replace_unprintable(p_str, '?');
str_append_text(p_str, ": BUG: string is too long");
bug(p_str->p_buf);
}
unsigned int* ups = vsf_sysutil_malloc(ups_size);
unsigned int up_count = 0;
for (unsigned int i=0; i < p_str->len; i++)
{
if (!vsf_sysutil_isprint(p_str->p_buf[i]))
{
ups[up_count++] = i;
}
}
str_replace_positions_with_hex(p_str, ups, up_count);
vsf_sysutil_free(ups);
}
void str_replace_unprintable_with_hex_wc(struct mystr* p_str)
{
unsigned int ups_size = sizeof(unsigned int) * (p_str->len);
if (ups_size < p_str->len)
{
str_replace_unprintable(p_str, '?');
str_append_text(p_str, ": BUG: string is too long");
bug(p_str->p_buf);
}
unsigned int* ups = vsf_sysutil_malloc(ups_size);
unsigned int up_count = 0;
size_t current = 0;
wchar_t pwc;
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
ssize_t len = 0;
while ((len = mbrtowc(&pwc, p_str->p_buf, p_str->len - current, &ps)) > 0)
{
if (!iswprint(pwc))
{
for (int i = 0; i < len; i++)
{
ups[up_count++] = current++;
}
}
else
{
current += len;
}
}
if (len < 0)
{
while (current < p_str->len)
{
ups[up_count++] = current++;
}
}
str_replace_positions_with_hex(p_str, ups, up_count);
vsf_sysutil_free(ups);
}
void
str_replace_positions_with_hex(struct mystr* p_str, const unsigned int* poss, const unsigned int pos_count)
{
if (pos_count == 0)
return;
struct mystr tmp_str = INIT_MYSTR;
str_reserve(&tmp_str, p_str->len + 3 * pos_count);
unsigned int current = 0;
for (unsigned int i=0; i < pos_count; i++)
{
unsigned int pos = poss[i];
if (current < pos)
private_str_append_memchunk(&tmp_str, p_str->p_buf + current, pos - current);
char hex_buf[5];
memset(hex_buf, 0, sizeof(hex_buf));
sprintf(hex_buf, "\\x%02X", (unsigned char) p_str->p_buf[pos]);
str_append_text(&tmp_str, hex_buf);
current = pos + 1;
}
if (current < p_str->len)
private_str_append_memchunk(&tmp_str, p_str->p_buf + current, p_str->len - current);
str_copy(p_str, &tmp_str);
str_free(&tmp_str);
}
void
str_basename (struct mystr* d_str, const struct mystr* path)
{
static struct mystr tmp;
str_copy (&tmp, path);
str_split_char_reverse(&tmp, d_str, '/');
if (str_isempty(d_str))
str_copy (d_str, path);
}