/*
	xterm 256 color code parser by Igor van den Hoven

	v1.0 02/11/2009
	v1.4 06/24/2011  v1.4.2 06/30/2011

	This code is placed in the public domain.
*/

/*
	For xterm 256 color foreground colors use:
	
	<aaa> to <fff> for RGB foreground colors

	<g00> to <g23> for grayscale foreground colors


	For xterm 256 color background colors use:

	<AAA> to <FFF> for RGB background colors

	<G00> to <G23> for grayscale background colors

	Background color support is commented out by default


	With 256 colors disabled colors are converted to 16 color ANSI.
*/

/*
	For 32 color codes use:

	^a - dark azure                 ^A - azure
	^b - dark blue                  ^B - blue
	^c - dark cyan                  ^C - cyan
	^e - dark ebony                 ^E - ebony
	^g - dark green                 ^G - green
	^j - dark jade                  ^J - jade
	^l - dark lime                  ^L - lime
	^m - dark magenta               ^M - magenta
	^o - dark orange                ^O - orange
	^p - dark pink                  ^P - pink
	^r - dark red                   ^R - red
	^s - dark silver                ^S - silver
	^t - dark tan                   ^T - tan
	^v - dark violet                ^V - violet
	^w - dark white                 ^W - white
	^y - dark yellow                ^Y - yellow


	With 256 colors disabled colors are converted to 16 color ANSI.
*/

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

/*
	256 to 16 color conversion table
*/

char *ansi_colors[256] =
{
	"\e[22;30m", "\e[22;31m", "\e[22;32m", "\e[22;33m", "\e[22;34m", "\e[22;35m", "\e[22;36m", "\e[22;37m",
	"\e[1;30m",  "\e[1;31m",  "\e[1;32m",  "\e[1;33m",  "\e[1;34m",  "\e[1;35m",  "\e[1;36m",  "\e[1;37m",

	"\e[22;30m", "\e[22;34m", "\e[22;34m", "\e[22;34m", "\e[1;34m",  "\e[1;34m",
	"\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;34m", "\e[1;34m",  "\e[1;34m",
	"\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;36m", "\e[1;34m",  "\e[1;34m",
	"\e[22;32m", "\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;36m", "\e[1;36m",
	"\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[22;36m", "\e[1;36m",  "\e[1;36m",
	"\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",  "\e[1;36m",

	"\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;34m", "\e[1;34m",  "\e[1;34m",
	"\e[22;33m", "\e[1;30m",  "\e[22;34m", "\e[22;34m", "\e[1;34m",  "\e[1;34m",
	"\e[22;33m", "\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[1;34m",  "\e[1;34m",
	"\e[22;32m", "\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;36m", "\e[1;36m",
	"\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[22;36m", "\e[1;36m",  "\e[1;36m",
	"\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",  "\e[1;36m",

	"\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;35m", "\e[1;34m",  "\e[1;34m",
	"\e[22;33m", "\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[1;34m",  "\e[1;34m",
	"\e[22;33m", "\e[22;33m", "\e[22;37m", "\e[22;34m", "\e[1;34m",  "\e[1;34m",
	"\e[22;33m", "\e[22;33m", "\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[1;34m",
	"\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[22;36m", "\e[1;36m",  "\e[1;36m",
	"\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",

	"\e[22;31m", "\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;35m", "\e[1;35m",
	"\e[22;31m", "\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;35m", "\e[1;35m",
	"\e[22;33m", "\e[22;33m", "\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[1;34m",
	"\e[22;33m", "\e[22;33m", "\e[22;33m", "\e[22;37m", "\e[1;34m",  "\e[1;34m",
	"\e[22;33m", "\e[22;33m", "\e[22;33m", "\e[1;32m",  "\e[1;36m",  "\e[1;36m",
	"\e[1;33m",  "\e[1;33m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",

	"\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[22;35m", "\e[1;35m",  "\e[1;35m",
	"\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[22;35m", "\e[1;35m",  "\e[1;35m",
	"\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[22;35m", "\e[1;35m",  "\e[1;35m",
	"\e[22;33m", "\e[22;33m", "\e[22;33m", "\e[1;31m",  "\e[1;35m",  "\e[1;35m",
	"\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",
	"\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",

	"\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",  "\e[1;35m",
	"\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",  "\e[1;35m",
	"\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",
	"\e[1;33m",  "\e[1;33m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",
	"\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",
	"\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",

	"\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",
	"\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",
	"\e[22;37m", "\e[22;37m", "\e[22;37m", "\e[22;37m", "\e[22;37m", "\e[22;37m",
	"\e[1;37m",  "\e[1;37m",  "\e[1;37m",  "\e[1;37m",  "\e[1;37m",  "\e[1;37m"
};

/*
	32 to 256 color conversion table
*/

char *alphabet_colors_dark[26] =
{
	"<abd>", "<aad>", "<add>", "", "<g04>", "", "<ada>", "", "", "<adb>", "", "<bda>", "<dad>",
	"", "<dba>", "<dab>", "", "<daa>", "<ccc>", "<cba>", "", "<bad>", "<ddd>", "", "<dda>", ""
};

char *alphabet_colors_bold[26] =
{
	"<acf>", "<aaf>", "<aff>", "", "<bbb>", "", "<afa>", "", "", "<afc>", "", "<cfa>", "<faf>",
	"", "<fca>", "<fac>", "", "<faa>", "<eee>", "<eda>", "", "<caf>", "<fff>", "", "<ffa>", ""
};


// Make sure that the output buffer remains at least 4 times larger than the user input buffer.

// colors should either be 0 for no colors, 16 for ansi colors, or 256 for xterm 256 colors.

int substitute_color(char *input, char *output, int colors)
{
	char *pti, *pto, old[6] = { 0 };

	pti = input;
	pto = output;

	while (*pti)
	{
		switch (*pti)
		{
			case '^':
				if (isalpha(pti[1]))
				{
					if (pti[2] == '^' && isalpha(pti[3]))
					{
						pti += 2;
						continue;
					}

					if (strncmp(old, pti, 2) && colors)
					{
						if (pti[1] >= 'a' && pti[1] <= 'z')
						{
							pto += substitute_color(alphabet_colors_dark[pti[1] - 'a'], pto, colors);
						}
						else
						{
							pto += substitute_color(alphabet_colors_bold[pti[1] - 'A'], pto, colors);
						}
					}
					pti += sprintf(old, "%c%c", pti[0], pti[1]);
				}
				else
				{
					if (pti[1] == '^')
					{
						pti++;
					}
					*pto++ = *pti++;
				}
				break;

			case '<':
				if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f' && pti[4] == '>')
				{
					if (strncmp(old, pti, 5) && colors)
					{
						if (colors == 256)
						{
							pto += sprintf(pto, "\033[38;5;%dm", 16 + (pti[1] - 'a') * 36 + (pti[2] - 'a') * 6 + (pti[3] - 'a'));
						}
						else
						{
							pto += substitute_color(ansi_colors[16 + (pti[1] - 'a') * 36 + (pti[2] - 'a') * 6 + (pti[3] - 'a')], pto, colors);
						}
					}
					pti += sprintf(old, "<%c%c%c>", pti[1], pti[2], pti[3]);
				}
/*
				else if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F' && pti[4] == '>')
				{
					if (colors == 256)
					{
						pto += sprintf(pto, "\033[48;5;%dm", 16 + (pti[1] - 'A') * 36 + (pti[2] - 'A') * 6 + (pti[3] - 'A'));
					}
					else if (colors)
					{
						pto += sprintf(pto, "\033[4%dm", (pti[1] && pti[1] >= pti[2] ? pti[1] >= pti[3] : 0) + (pti[2] && pti[2] >= pti[1] ? pti[2] >= pti[3] : 0) * 2 + (pti[3] && pti[3] >= pti[2] ? pti[3] >= pti[1] : 0) * 4);
					}
					pti += 5;
				}
*/
				else if (pti[1] == 'g' && isdigit((int) pti[2]) && isdigit((int) pti[3]) && (pti[2] - '0') * 10 + (pti[3] - '0') >= 0 && (pti[2] - '0') * 10 + (pti[3] - '0') < 24 && pti[4] == '>')
				{
					if (strncmp(old, pti, 5) && colors)
					{
						if (colors == 256)
						{
							pto += sprintf(pto, "\033[38;5;%dm", 232 + (pti[2] - '0') * 10 + (pti[3] - '0'));
						}
						else
						{
							pto += substitute_color(ansi_colors[232 + (pti[2] - '0') * 10 + (pti[3] - '0')], pto, colors);
						}
					}
					pti += sprintf(old, "<%c%c%c>", pti[1], pti[2], pti[3]);
				}
/*
				else if (pti[1] == 'G' && isdigit((int) pti[2]) && isdigit((int) pti[3]) && (pti[2] - '0') * 10 + (pti[3] - '0') >= 0 && (pti[2] - '0') * 10 + (pti[3] - '0') < 24 && pti[4] == '>')
				{
					if (colors == 256)
					{
						pto += sprintf(pto, "\033[48;5;%dm", 232 + (pti[2] - '0') * 10 + (pti[3] - '0'));
					}
					else if (colors)
					{
						pto += sprintf(pto, "\033[4%dm", ((pti[2] - '0') * 10 + (pti[3] - '0')) / 12 ? 7 : 0);
					}
					pti += 5;
				}
*/
				else
				{
					*pto++ = *pti++;
				}
				break;

			default:
				*pto++ = *pti++;
				break;
		}
	}
	*pto = 0;

	return pto - output;
}

/*
	Compile for an example display
*/

int main(int argc, char **argv)
{
	char buf[8000], tmp[8000], out[8000];
	int x, y, z;

	/*
		Foreground
	*/

	for (x = 'a' ; x <= 'f' ; x++)
	{
		for (y = 'a' ; y <= 'f' ; y++)
		{
			buf[0] = 0;

			for (z = 'a' ; z <= 'f' ; z++)
			{
				sprintf(tmp, "<%c%c%c><<%c%c%c>%c%c%c><aaa> ", x, y, z, x, y, z, x, y, z);
				strcat(buf, tmp);
			}
			substitute_color(buf, out, 256);
			printf("%s  ", out);

			substitute_color(buf, out, 16);
			printf("%s\n", out);
		}
	}

	buf[0] = 0;

	for (x = 0 ; x < 24 ; x++)
	{
		sprintf(tmp, "<g%02d>%02d<ddd> ", x, x);
		strcat(buf, tmp);
	}

	substitute_color(buf, out, 256);
	printf("\n%s\n", out);

	substitute_color(buf, out, 16);
	printf("%s\n\n", out);

substitute_color(
	"        ^a - dark azure                 ^A - azure\n"
	"        ^b - dark blue                  ^B - blue\n"
	"        ^c - dark cyan                  ^C - cyan\n"
	"        ^e - dark ebony (black)         ^E - ebony\n"
	"        ^g - dark green                 ^G - green\n"
	"        ^j - dark jade                  ^J - jade\n"
	"        ^l - dark lime (chartreuse)     ^L - lime\n"
	"        ^m - dark magenta               ^M - magenta\n"
	"        ^o - dark orange                ^O - orange\n"
	"        ^p - dark pink (rose)           ^P - pink\n"
	"        ^r - dark red                   ^R - red\n"
	"        ^s - dark silver                ^S - silver\n"
	"        ^t - dark tan (brown)           ^T - tan\n"
	"        ^v - dark violet                ^V - violet\n"
	"        ^w - dark white                 ^W - white\n"
	"        ^y - dark yellow                ^Y - yellow\n", out, 256);

	printf("%s\n", out);

	return 0;
}