#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*
 * Cheesy number->string conversion, complete with carry rounding error.
 */
char *num2str(unsigned long num, int maxlen, int base, int pow2, int unit_base)
{
	const char *postfix[] = { "", "K", "M", "G", "P", "E" };
	const char *byte_postfix[] = { "", "B", "bit" };
	const unsigned int thousand[] = { 1000, 1024 };
	unsigned int modulo, decimals;
	int byte_post_index = 0, post_index, carry = 0;
	char tmp[32];
	char *buf;

	buf = malloc(128);

	for (post_index = 0; base > 1; post_index++)
		base /= thousand[!!pow2];

	switch (unit_base) {
	case 1:
		byte_post_index = 2;
		num *= 8;
		break;
	case 8:
		byte_post_index = 1;
		break;
	}

	modulo = -1U;
	while (post_index < sizeof(postfix)) {
		sprintf(tmp, "%lu", num);
		if (strlen(tmp) <= maxlen)
			break;

		modulo = num % thousand[!!pow2];
		num /= thousand[!!pow2];
		carry = modulo >= thousand[!!pow2] / 2;
		post_index++;
	}

	if (modulo == -1U) {
done:
		sprintf(buf, "%lu%s%s", num, postfix[post_index],
			byte_postfix[byte_post_index]);
		return buf;
	}

	sprintf(tmp, "%lu", num);
	decimals = maxlen - strlen(tmp);
	if (decimals <= 1) {
		if (carry)
			num++;
		goto done;
	}

	do {
		sprintf(tmp, "%u", modulo);
		if (strlen(tmp) <= decimals - 1)
			break;

		modulo = (modulo + 9) / 10;
	} while (1);

	sprintf(buf, "%lu.%u%s%s", num, modulo, postfix[post_index],
		byte_postfix[byte_post_index]);
	return buf;
}
