view src/lib/strfuncs.c @ 3014:f23e9957544f HEAD

Added strarray_length().
author Timo Sirainen <tss@iki.fi>
date Sun, 26 Dec 2004 11:09:25 +0200
parents d141e1bfdd63
children 2f7ffdcd7d67
line wrap: on
line source

/* Copyright (c) 2002-2003 Timo Sirainen */

/* @UNSAFE: whole file */

#include "lib.h"
#include "printf-upper-bound.h"
#include "strfuncs.h"

#include <stdio.h>
#include <limits.h>
#include <ctype.h>

#define STRCONCAT_BUFSIZE 512

static const char *fix_format_real(const char *fmt, const char *p)
{
	const char *errstr;
	char *buf;
	size_t pos, alloc, errlen;

	errstr = strerror(errno);
	errlen = strlen(errstr);

	pos = (size_t) (p-fmt);
	i_assert(pos < SSIZE_T_MAX);

	alloc = pos + errlen + 128;
	buf = t_buffer_get(alloc);

	memcpy(buf, fmt, pos);

	while (*p != '\0') {
		if (*p == '%' && p[1] == 'm') {
			if (pos+errlen+1 > alloc) {
				alloc += errlen+1 + 128;
				buf = t_buffer_get(alloc);
			}

			memcpy(buf+pos, errstr, errlen);
			pos += errlen;
			p += 2;
		} else {
			/* p + \0 */
			if (pos+2 > alloc) {
				alloc += 128;
				buf = t_buffer_get(alloc);
			}

			buf[pos++] = *p;
			p++;
		}
	}

	buf[pos++] = '\0';
	t_buffer_alloc(pos);
	return buf;
}

/* replace %m with strerror() */
const char *printf_string_fix_format(const char *fmt)
{
	const char *p;

	for (p = fmt; *p != '\0'; p++) {
		if (*p == '%' && p[1] == 'm')
			return fix_format_real(fmt, p);
	}

	return fmt;
}

int i_snprintf(char *dest, size_t max_chars, const char *format, ...)
{
#ifndef HAVE_VSNPRINTF
	char *buf;
#endif
	va_list args, args2;
	ssize_t len;
	int ret;

	i_assert(max_chars < INT_MAX);

	t_push();

	va_start(args, format);
	VA_COPY(args2, args);

	len = printf_string_upper_bound(&format, args);

	i_assert(len >= 0);

#ifdef HAVE_VSNPRINTF
	len = vsnprintf(dest, max_chars, format, args2);
#else
	buf = t_buffer_get(len);
	len = vsprintf(buf, format, args2);
#endif
	va_end(args);

	if (len < 0) {
		/* some error occured */
		len = 0;
		ret = -1;
	} else if ((size_t)len >= max_chars) {
		/* too large */
		len = max_chars-1;
		ret = -1;
	} else {
		ret = 0;
	}

#ifndef HAVE_VSNPRINTF
	memcpy(dest, buf, len);
#endif
	dest[len] = '\0';

	t_pop();
	return ret;
}

char *p_strdup(pool_t pool, const char *str)
{
	void *mem;
	size_t len;

	if (str == NULL)
                return NULL;

	for (len = 0; (str)[len] != '\0'; )
		len++;
	len++;

	mem = p_malloc(pool, len);
	memcpy(mem, str, len);
	return mem;
}

char *p_strdup_empty(pool_t pool, const char *str)
{
	if (str == NULL || *str == '\0')
                return NULL;

	return p_strdup(pool, str);
}

char *p_strdup_until(pool_t pool, const void *start, const void *end)
{
	size_t size;
	char *mem;

	i_assert((const char *) start <= (const char *) end);

	size = (size_t) ((const char *) end - (const char *) start);

	mem = p_malloc(pool, size + 1);
	memcpy(mem, start, size);
	return mem;
}

char *p_strndup(pool_t pool, const void *str, size_t max_chars)
{
	char *mem;
	size_t len;

	i_assert(max_chars != (size_t)-1);

	if (str == NULL)
		return NULL;

	len = 0;
	while (len < max_chars && ((const char *) str)[len] != '\0')
		len++;

	mem = p_malloc(pool, len+1);
	memcpy(mem, str, len);
	mem[len] = '\0';
	return mem;
}

char *p_strdup_printf(pool_t pool, const char *format, ...)
{
	va_list args;
        char *ret;

	va_start(args, format);
        ret = p_strdup_vprintf(pool, format, args);
	va_end(args);

	return ret;
}

char *p_strdup_vprintf(pool_t pool, const char *format, va_list args)
{
	char *ret;
	va_list args2;
	size_t len;

	i_assert(format != NULL);

	if (!pool->datastack_pool)
		t_push();

	VA_COPY(args2, args);

	len = printf_string_upper_bound(&format, args);
        ret = p_malloc(pool, len);

#ifdef HAVE_VSNPRINTF
	vsnprintf(ret, len, format, args2);
#else
	vsprintf(ret, format, args2);
#endif
	if (!pool->datastack_pool)
		t_pop();
	return ret;
}

const char *_vstrconcat(const char *str1, va_list args, size_t *ret_len)
{
	const char *str;
        char *temp;
	size_t bufsize, i, len;

	if (str1 == NULL)
		return NULL;

	str = str1;
	bufsize = STRCONCAT_BUFSIZE;
	temp = t_buffer_get(bufsize);

	i = 0;
	do {
		len = strlen(str);

		if (i + len >= bufsize) {
			/* need more memory */
			bufsize = nearest_power(i + len + 1);
			temp = t_buffer_reget(temp, bufsize);
		}

		memcpy(temp + i, str, len); i += len;

		/* next string */
		str = va_arg(args, const char *);
	} while (str != NULL);

	i_assert(i < bufsize);

	temp[i++] = '\0';
        *ret_len = i;
        return temp;
}

char *p_strconcat(pool_t pool, const char *str1, ...)
{
	va_list args;
        const char *temp;
	char *ret;
        size_t len;

	va_start(args, str1);

	temp = _vstrconcat(str1, args, &len);
	if (temp == NULL)
		ret = NULL;
	else {
		ret = p_malloc(pool, len);
		memcpy(ret, temp, len);
	}

	va_end(args);
        return ret;
}

const char *t_strdup(const char *str)
{
	return p_strdup(unsafe_data_stack_pool, str);
}

char *t_strdup_noconst(const char *str)
{
	return p_strdup(unsafe_data_stack_pool, str);
}

const char *t_strdup_empty(const char *str)
{
	return p_strdup_empty(unsafe_data_stack_pool, str);
}

const char *t_strdup_until(const void *start, const void *end)
{
	return p_strdup_until(unsafe_data_stack_pool, start, end);
}

const char *t_strndup(const void *str, size_t max_chars)
{
	return p_strndup(unsafe_data_stack_pool, str, max_chars);
}

const char *t_strdup_printf(const char *format, ...)
{
	va_list args;
	const char *ret;

	va_start(args, format);
	ret = p_strdup_vprintf(unsafe_data_stack_pool, format, args);
	va_end(args);

	return ret;
}

const char *t_strdup_vprintf(const char *format, va_list args)
{
	return p_strdup_vprintf(unsafe_data_stack_pool, format, args);
}

const char *t_strconcat(const char *str1, ...)
{
	va_list args;
	const char *ret;
        size_t len;

	va_start(args, str1);

	ret = _vstrconcat(str1, args, &len);
	if (ret != NULL)
		t_buffer_alloc(len);

	va_end(args);
        return ret;
}

const char *t_strcut(const char *str, char cutchar)
{
	const char *p;

	for (p = str; *p != '\0'; p++) {
		if (*p == cutchar)
                        return t_strdup_until(str, p);
	}

        return str;
}

int is_numeric(const char *str, char end_char)
{
	if (*str == '\0' || *str == end_char)
		return FALSE;

	while (*str != '\0' && *str != end_char) {
		if (!i_isdigit(*str))
			return FALSE;
		str++;
	}

	return TRUE;
}

int strocpy(char *dest, const char *src, size_t dstsize)
{
	if (dstsize == 0)
		return -1;

	while (*src != '\0' && dstsize > 1) {
		*dest++ = *src++;
		dstsize--;
	}

	*dest++ = '\0';
	return *src == '\0' ? 0 : -1;
}

int str_path(char *dest, size_t dstsize, const char *dir, const char *file)
{
	size_t dirlen, filelen;

	dirlen = strlen(dir);
	filelen = strlen(file);

	if (dirlen+1+filelen >= dstsize) {
		if (dstsize > 0)
			*dest = '\0';
		errno = ENAMETOOLONG;
		return -1;
	}

	memcpy(dest, dir, dirlen);
	dest[dirlen] = '/';
	memcpy(dest + dirlen + 1, file, filelen);
	dest[dirlen + 1 + filelen] = '\0';
	return 0;
}

int str_ppath(char *dest, size_t dstsize, const char *dir,
	      const char *file_prefix, const char *file)
{
	size_t dirlen, prefixlen, filelen;

	dirlen = strlen(dir);
	prefixlen = strlen(file_prefix);
	filelen = strlen(file);

	if (dirlen+1+prefixlen+filelen >= dstsize) {
		if (dstsize > 0)
			*dest = '\0';
		errno = ENAMETOOLONG;
		return -1;
	}

	memcpy(dest, dir, dirlen);
	dest[dirlen] = '/';
	memcpy(dest + dirlen + 1, file_prefix, prefixlen);
	memcpy(dest + dirlen + prefixlen + 1, file, filelen);
	dest[dirlen + 1 + prefixlen + filelen] = '\0';
	return 0;
}

char *str_ucase(char *str)
{
	char *p;

	for (p = str; *p != '\0'; p++)
		*p = i_toupper(*p);
        return str;
}

char *str_lcase(char *str)
{
	char *p;

	for (p = str; *p != '\0'; p++)
		*p = i_tolower(*p);
        return str;
}

const char *t_str_lcase(const char *str)
{
	return str_lcase(t_strdup_noconst(str));
}

const char *t_str_ucase(const char *str)
{
	return str_ucase(t_strdup_noconst(str));
}

int null_strcmp(const char *s1, const char *s2)
{
	if (s1 == NULL)
		return s2 == NULL ? 0 : -1;
	if (s2 == NULL)
		return 1;

	return strcmp(s1, s2);
}

int memcasecmp(const void *p1, const void *p2, size_t size)
{
	const unsigned char *s1 = p1;
	const unsigned char *s2 = p2;
	int ret;

	while (size > 0) {
		ret = i_toupper(*s1) - i_toupper(*s2);
		if (ret != 0)
			return ret;

		s1++; s2++; size--;
	}

        return 0;
}

int bsearch_strcasecmp(const void *p1, const void *p2)
{
	const char *key = p1;
	const char *const *member = p2;

	return strcasecmp(key, *member);
}

int strcasecmp_p(const void *p1, const void *p2)
{
	const char *const *s1 = p1, *const *s2 = p2;

	return strcasecmp(*s1, *s2);
}

static const char **_strsplit(const char *data, const char *separators,
			      int spaces)
{
        const char **array;
	char *str;
        size_t alloc_len, len;

	i_assert(*separators != '\0');

	if (spaces)
		while (*data == ' ') data++;
	if (*data == '\0')
		return t_new(const char *, 1);

	str = t_strdup_noconst(data);

        alloc_len = 32;
        array = t_buffer_get(sizeof(const char *) * alloc_len);

	array[0] = str; len = 1;
	while (*str != '\0') {
		if (strchr(separators, *str) != NULL) {
			/* separator found */
			if (len+1 >= alloc_len) {
                                alloc_len = nearest_power(alloc_len+1);
				array = t_buffer_reget(array,
						       sizeof(const char *) *
						       alloc_len);
			}

			if (*str != ' ' || !spaces)
				*str = '\0';
			else {
				*str = '\0';
				while (str[1] == ' ') str++;
			}

			array[len++] = str+1;
		}

                str++;
	}

	i_assert(len < alloc_len);
        array[len] = NULL;

	t_buffer_alloc(sizeof(const char *) * (len+1));
        return array;
}

const char **t_strsplit(const char *data, const char *separators)
{
	return _strsplit(data, separators, FALSE);
}

const char **t_strsplit_spaces(const char *data, const char *separators)
{
	return _strsplit(data, separators, TRUE);
}

unsigned int strarray_length(const char *const *arr)
{
	unsigned int count;

	if (arr == NULL)
		return 0;

	for (count = 0; *arr != NULL; arr++)
		count++;

	return count;
}

const char *dec2str(uintmax_t number)
{
	char *buffer;
	int pos;

	pos = MAX_INT_STRLEN;
	buffer = t_malloc(pos);

	buffer[--pos] = '\0';
	do {
		buffer[--pos] = (number % 10) + '0';
		number /= 10;
	} while (number != 0 && pos >= 0);

	i_assert(pos >= 0);
	return buffer + pos;
}