aboutsummaryrefslogtreecommitdiff
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <unistd.h>

#define MINIMUM_EXTEND_BYTES 63

int extend_buf(char **buf, size_t *buf_len, size_t *buf_filled,
	       size_t extend_len)
{
	ssize_t space_left = *buf_len - *buf_filled - 1;
	size_t new_size, size_required, size_more_space;
	char *new_buf;

	if (space_left >= 0 && space_left >= extend_len)
		return 0;

	size_required = *buf_filled + extend_len + 1;
	size_more_space = size_required + MINIMUM_EXTEND_BYTES;
	new_size = *buf_len * 2;
	if (new_size < size_more_space)
		new_size = size_more_space;

	new_buf = realloc(*buf, new_size);
	if (!new_buf)
		new_buf = realloc(*buf, size_required);
	if (!new_buf)
		return -1;

	*buf = new_buf;
	*buf_len = new_size;
	return 0;
}

int sb_bytes(char **buf, size_t *buf_len, size_t *buf_filled,
	     const unsigned char *bytes, size_t bytes_len)
{
	if (extend_buf(buf, buf_len, buf_filled, bytes_len))
		return -1;

	memcpy(*buf + *buf_filled, bytes, bytes_len);
	*(*buf + *buf_filled + bytes_len) = '\0';
	*buf_filled += bytes_len;
	return 0;
}

int sb_string(char **buf, size_t *buf_len, size_t *buf_filled,
	      const char *string)
{
	size_t string_len = strlen(string);

	return sb_bytes(buf, buf_len, buf_filled,
			(const unsigned char*) string, string_len);
}

int sb_char(char **buf, size_t *buf_len, size_t *buf_filled, char c)
{
	return sb_bytes(buf, buf_len, buf_filled,
			(const unsigned char*) &c, 1);
}

/* Below is old version */

/* inline static size_t process_fmt(MYSQL *mysql, const char *fmt, */
/* 				 va_list ap, char *dst) */
/* { */
/* 	size_t len = 0; */
/* 	const char *in_pos = fmt; */
/* 	char *out_pos = dst, c; */
/* 	bool percent = false; */

/* 	short digits_count; */
/* 	long power_of_10; */
/* 	long num_arg; */

/* 	const char *string_arg; */
/* 	size_t string_len; */

/* #define WRITE_CHAR(ch)				\ */
/* 	do {					\ */
/* 		if (dst)			\ */
/* 			*(out_pos++) = (ch);	\ */
/* 		else				\ */
/* 			len++;			\ */
/* 	}					\ */
/* 	while (0)				\ */

/* 	while (*in_pos) { */
/* 		c = *(in_pos++); */

/* 		if (!percent) { */
/* 			if (c != '%') */
/* 				WRITE_CHAR(c); */
/* 			else */
/* 				percent = true; */

/* 			continue; */
/* 		} */

/* 		percent = false; */

/* 		switch (c) { */
/* 		case 'd': */
/* 		case 'u': */
/* 			if (c == 'd') */
/* 				num_arg = va_arg(ap, int); */
/* 			else */
/* 				num_arg = va_arg(ap, unsigned); */

/* 			if (num_arg < 0) { */
/* 				WRITE_CHAR('-'); */
/* 				num_arg = -num_arg; */
/* 			} */
/* 			for (digits_count = 1, power_of_10 = 10; */
/* 			     power_of_10 <= num_arg; */
/* 			     digits_count++, power_of_10 *= 10); */

/* 			power_of_10 /= 10; */
/* 			while (digits_count--) { */
/* 				WRITE_CHAR('0' + num_arg / power_of_10); */
/* 				num_arg = (num_arg % power_of_10) * 10; */
/* 			} */
/* 			break; */
/* 		case 's': */
/* 			for (string_arg = va_arg(ap, const char*); */
/* 			     *string_arg; string_arg++) */
/* 				WRITE_CHAR(*string_arg); */
/* 			break; */
/* 		case 'm': */
/* 			string_arg = va_arg(ap, const char*); */
/* 			string_len = strlen(string_arg); */
/* 			if (!dst) { */
/* 				len += 2 * string_len; */
/* 				break; */
/* 			} */
/* 			out_pos += mysql_real_escape_string(mysql, out_pos, */
/* 							    string_arg, */
/* 							    string_len); */
/* 			break; */
/* 		case '%': */
/* 			WRITE_CHAR('%'); */
/* 		} */
/* 	} */

/* 	if (dst) { */
/* 		*out_pos = '\0'; */
/* 		return out_pos - dst; */
/* 	} */
/* 	return len; */
/* } */

/* int sb_sprintf(char **buf, size_t *buf_len, size_t *buf_filled, */
/* 		   MYSQL *mysql, const char *fmt, ...) */
/* { */
/* 	size_t extend_len; */
/* 	va_list ap; */

/* 	va_start(ap, fmt); */
/* 	extend_len = process_fmt(NULL, fmt, ap, NULL); */
/* 	va_end(ap); */

/* 	if (extend_buf(buf, buf_len, buf_filled, extend_len)) */
/* 		return -1; */

/* 	va_start(ap, fmt); */
/* 	*buf_filled += process_fmt(mysql, fmt, ap, *buf + *buf_filled); */
/* 	va_end(ap); */

/* 	return 0; */
/* } */

int sb_num(char **buf, size_t *buf_len, size_t *buf_filled, long num)
{
	unsigned char repr[3 * sizeof(long) + 1];
	int i;
	bool neg = num < 0;

	for (i = sizeof(repr); num; num /= 10)
		repr[--i] = '0' + num % 10;

	if (i == sizeof(repr))
		repr[--i] = '0';
	else if (neg)
		repr[--i] = '-';

	sb_bytes(buf, buf_len, buf_filled,
		 repr + i, sizeof(repr) - i);

	return 0;
}

int sb_vsprintf(char **buf, size_t *buf_len, size_t *buf_filled,
		const char *fmt, va_list ap)
{
	const unsigned char *in_pos = (const unsigned char*) fmt;
	char c;
	size_t i = 0;
	bool percent = false;

	long num_arg;
	int (*sb_cb)(char**, size_t*, size_t*, void*);

	while (in_pos[i]) {
		c = in_pos[i++];

		if (!percent) {
			if (c == '%') {
				percent = true;
				if (sb_bytes(buf, buf_len, buf_filled,
					     in_pos, i - 1))
					return -1;
			}

			continue;
		}

		percent = false;
		in_pos += i;
		i = 0;

		switch (c) {
		case 'd':
		case 'u':
			num_arg = c == 'd' ?
				va_arg(ap, int) : va_arg(ap, unsigned);

			if (sb_num(buf, buf_len, buf_filled, num_arg))
				return -1;
			break;
		case 's':
			if (sb_string(buf, buf_len, buf_filled,
				      va_arg(ap, const char*)))
				return -1;
			break;
		case '_':
			sb_cb = va_arg(ap, int (*)(char**, size_t*,
						   size_t*, void*));
			if (sb_cb(buf, buf_len, buf_filled,
				  va_arg(ap, void*)))
				return -1;
			break;
		case '%':
			in_pos--;
			i++;
		}
	}

	if (!percent && sb_bytes(buf, buf_len, buf_filled, in_pos, i))
		return -1;

	return 0;
}

int sb_sprintf(char **buf, size_t *buf_len, size_t *buf_filled,
	       const char *fmt, ...)
{
	va_list ap;
	int res;

	va_start(ap, fmt);
	res = sb_vsprintf(buf, buf_len, buf_filled, fmt, ap);
	va_end(ap);

	return res;
}

int crop_buf(char **buf, size_t *buf_len, size_t *buf_filled)
{
	char *new_buf;

	if (*buf_len <= *buf_filled + 1)
		return 0;

	new_buf = realloc(*buf, *buf_filled + 1);
	if (!new_buf)
		return -1;

	*buf = new_buf;
	*buf_len = *buf_filled + 1;
	return 0;
}