view src/lib/strescape.c @ 23007:36e01285b5b8

lib: buffer - Improve header comment for buffer_insert() and buffer_delete().
author Stephan Bosch <stephan.bosch@dovecot.fi>
date Mon, 18 Mar 2019 00:52:37 +0100
parents cb108f786fb4
children
line wrap: on
line source

/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "str.h"
#include "strescape.h"

const char *str_escape(const char *str)
{
	const char *p;
	string_t *ret;

	/* see if we need to quote it */
	for (p = str; *p != '\0'; p++) {
		if (IS_ESCAPED_CHAR(*p))
			break;
	}

	if (*p == '\0')
		return str;

	/* quote */
	ret = t_str_new((size_t) (p - str) + 128);
	str_append_n(ret, str, (size_t) (p - str));

	for (; *p != '\0'; p++) {
		if (IS_ESCAPED_CHAR(*p))
			str_append_c(ret, '\\');
		str_append_c(ret, *p);
	}
	return str_c(ret);
}

void str_append_unescaped(string_t *dest, const void *src, size_t src_size)
{
	const unsigned char *src_c = src;
	size_t start = 0, i = 0;

	while (i < src_size) {
		for (; i < src_size; i++) {
			if (src_c[i] == '\\')
				break;
		}

		str_append_n(dest, src_c + start, i-start);

		if (i < src_size) {
			if (++i == src_size)
				break;
			str_append_c(dest, src_c[i++]);
		}
		start = i;
	}
}

char *str_unescape(char *str)
{
	/* @UNSAFE */
	char *dest, *start = str;

	while (*str != '\\') {
		if (*str == '\0')
			return start;
		str++;
	}

	for (dest = str; *str != '\0'; str++) {
		if (*str == '\\') {
			str++;
			if (*str == '\0')
				break;
		}

		*dest++ = *str;
	}

	*dest = '\0';
	return start;
}

int str_unescape_next(const char **str, const char **unescaped_r)
{
	const char *p;
	char *escaped;
	bool esc_found = FALSE;

	for (p = *str; *p != '\0'; p++) {
		if (*p == '"')
			break;
		else if (*p == '\\') {
			if (p[1] == '\0')
				return -1;
			esc_found = TRUE;
			p++;
		}
	}
	if (*p != '"')
		return -1;
	escaped = p_strdup_until(unsafe_data_stack_pool, *str, p);
	*str = p+1;
	*unescaped_r = !esc_found ? escaped : str_unescape(escaped);
	return 0;
}

void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size)
{
	for (size_t i = 0; i < src_size; i++) {
		switch (src[i]) {
		case '\000':
			str_append_c(dest, '\001');
			str_append_c(dest, '0');
			break;
		case '\001':
			str_append_c(dest, '\001');
			str_append_c(dest, '1');
			break;
		case '\t':
			str_append_c(dest, '\001');
			str_append_c(dest, 't');
			break;
		case '\r':
			str_append_c(dest, '\001');
			str_append_c(dest, 'r');
			break;
		case '\n':
			str_append_c(dest, '\001');
			str_append_c(dest, 'n');
			break;
		default:
			str_append_c(dest, src[i]);
			break;
		}
	}
}

void str_append_tabescaped(string_t *dest, const char *src) {
	str_append_tabescaped_n(dest, (const unsigned char*)src, strlen(src));
}


const char *str_tabescape(const char *str)
{
	string_t *tmp;
	const char *p;

	for (p = str; *p != '\0'; p++) {
		if (*p <= '\r') {
			tmp = t_str_new(128);
			str_append_n(tmp, str, p-str);
			str_append_tabescaped(tmp, p);
			return str_c(tmp);
		}
	}
	return str;
}

void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size)
{
	const unsigned char *src_c = src;
	size_t start = 0, i = 0;

	while (i < src_size) {
		for (; i < src_size; i++) {
			if (src_c[i] == '\001')
				break;
		}

		str_append_n(dest, src_c + start, i-start);

		if (i < src_size) {
			i++;
			if (i < src_size) {
				switch (src_c[i]) {
				case '0':
					str_append_c(dest, '\000');
					break;
				case '1':
					str_append_c(dest, '\001');
					break;
				case 't':
					str_append_c(dest, '\t');
					break;
				case 'r':
					str_append_c(dest, '\r');
					break;
				case 'n':
					str_append_c(dest, '\n');
					break;
				default:
					str_append_c(dest, src_c[i]);
					break;
				}
				i++;
			}
		}
		start = i;
	}
}

char *str_tabunescape(char *str)
{
	/* @UNSAFE */
	char *dest, *start = str;

	str = strchr(str, '\001');
	if (str == NULL) {
		/* no unescaping needed */
		return start;
	}

	for (dest = str; *str != '\0'; str++) {
		if (*str != '\001')
			*dest++ = *str;
		else {
			str++;
			if (*str == '\0')
				break;
			switch (*str) {
			case '0':
				*dest++ = '\000';
				break;
			case '1':
				*dest++ = '\001';
				break;
			case 't':
				*dest++ = '\t';
				break;
			case 'r':
				*dest++ = '\r';
				break;
			case 'n':
				*dest++ = '\n';
				break;
			default:
				*dest++ = *str;
				break;
			}
		}
	}

	*dest = '\0';
	return start;
}

const char *t_str_tabunescape(const char *str)
{
	if (strchr(str, '\001') == NULL)
		return str;
	else
		return str_tabunescape(t_strdup_noconst(str));
}

const char *const *t_strsplit_tabescaped_inplace(char *data)
{
	/* @UNSAFE */
	char **array;
	unsigned int count, new_alloc_count, alloc_count;

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

	alloc_count = 32;
	array = t_malloc(sizeof(char *) * alloc_count);

	array[0] = data; count = 1;
	bool need_unescape = FALSE;
	while ((data = strpbrk(data, "\t\001")) != NULL) {
		/* separator or escape char found */
		if (*data == '\001') {
			need_unescape = TRUE;
			data++;
			continue;
		}
		if (count+1 >= alloc_count) {
			new_alloc_count = nearest_power(alloc_count+1);
			array = p_realloc(unsafe_data_stack_pool, array,
					  sizeof(char *) * alloc_count,
					  sizeof(char *) *
					  new_alloc_count);
			alloc_count = new_alloc_count;
		}
		*data++ = '\0';
		if (need_unescape) {
			str_tabunescape(array[count-1]);
			need_unescape = FALSE;
		}
		array[count++] = data;
	}
	if (need_unescape)
		str_tabunescape(array[count-1]);
	i_assert(count < alloc_count);
	array[count] = NULL;

	return (const char *const *)array;
}

char **p_strsplit_tabescaped(pool_t pool, const char *str)
{
	char **args;
	unsigned int i;

	args = p_strsplit(pool, str, "\t");
	for (i = 0; args[i] != NULL; i++)
		args[i] = str_tabunescape(args[i]);
	return args;
}

const char *const *t_strsplit_tabescaped(const char *str)
{
	return (void *)p_strsplit_tabescaped(unsafe_data_stack_pool, str);
}