const char * UsageLines [] = {
	"Usage: keytoppm (color key)=r,g,b (color key)=r,g,b ...",
	"where (color key) is a letter and r,g,b are decimal numbers between",
	"0 and 255 for the color the key is to represent.",
	"Reads input text from standard input and writes ppm image to standard",
	"output.  All input lines should be the same length and will be the",
	"image width.  A default gray will be used for unspecified keys.",
	"",
	"March 29, 2017.  For latest, see gopher://sdf.org/1/users/julianbr",
	};
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );


#define DefaultR 200
#define DefaultG 200
#define DefaultB 200


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


int ReadInputLine (int * EndOfInputPtr, char * * InputLinePtr)
	{
	struct Letter {
		char letter;
		struct Letter * next;
		} * Letters, * Letter, * nextLetter, * * LetterPtr;
	int MemoryOk, length, c, i;

	MemoryOk = 1;
	EndOfInputPtr [0] = 0;
	Letters = NULL;
	LetterPtr = & Letters;
	c = getchar ();
	while (c != EOF && c != '\n' && MemoryOk) {
		LetterPtr [0] = malloc (sizeof (LetterPtr [0] [0] ) );
		if (LetterPtr [0] == NULL) {
			fprintf (stderr, "***keytoppm: Not enough memory.\n");
			MemoryOk = 0;
			}
		else {
			LetterPtr [0]->letter = c;
			LetterPtr [0]->next = NULL;
			LetterPtr = & LetterPtr [0]->next;
			}
		c = getchar ();
		}
	length = 0;
	Letter = Letters;
	while (Letter != NULL) {
		length++;
		Letter = Letter->next;
		}
	if (c == EOF)
		EndOfInputPtr [0] = 1;
	InputLinePtr [0] = NULL;
	if (MemoryOk) {
		InputLinePtr [0] = malloc (length + 1);
		if (InputLinePtr [0] == NULL) {
			fprintf (stderr, "***keytoppm: Not enough memory.\n");
			MemoryOk = 0;
			}
		else {
			i = 0;
			Letter = Letters;
			while (i < length && Letter != NULL) {
				InputLinePtr [0] [i] = Letter->letter;
				Letter = Letter->next;
				i++;
				}
			InputLinePtr [0] [length] = '\0';
			}
		}
	Letter = Letters;
	while (Letter != NULL) {
		nextLetter = Letter->next;
		free (Letter);
		Letter = nextLetter;
		}

	return MemoryOk;
	}


int ReadInputLines (int * NumInputLinesPtr, char * * * InputLinesPtr)
	{
	struct Line {
		char * line;
		struct Line * next;
		} * Line, * Lines, * nextLine, * * LinePtr;
	char * InputLine;
	int MemoryOk, EndOfInput, length, i;

	MemoryOk = 1;
	EndOfInput = 0;
	NumInputLinesPtr [0] = 0;
	Lines = NULL;
	LinePtr = & Lines;
	while (MemoryOk && !EndOfInput) {
		if (ReadInputLine (& EndOfInput, & InputLine) ) {
			LinePtr [0] = malloc (sizeof (LinePtr [0] [0] ) );
			if (LinePtr [0] == NULL) {
				fprintf (stderr, "***keytoppm: Not enough memory.\n");
				MemoryOk = 0;
				}
			else {
				LinePtr [0]->line = InputLine;
				LinePtr [0]->next = NULL;
				LinePtr = & LinePtr [0]->next;
				}
			}
		else 
			MemoryOk = 0;
		}
	length = 0;
	Line = Lines;
	while (Line != NULL) {
		length++;
		Line = Line->next;
		}
	InputLinesPtr [0] = NULL;
	if (MemoryOk && length > 0) {
		InputLinesPtr [0] = malloc (length*sizeof (InputLinesPtr [0] [0] ) );
		if (InputLinesPtr [0] == NULL) {
			fprintf (stderr, "***keytoppm: Not enough memory.\n");
			MemoryOk = 0;
			}
		else {
			i = 0;
			Line = Lines;
			while (i < length && Line != NULL) {
				InputLinesPtr [0] [i] = Line->line;
				Line->line = NULL;
				i++;
				Line = Line->next;
				}
			}
		}
	Line = Lines;
	while (Line != NULL) {
		if (Line->line != NULL)
			free (Line->line);
		nextLine = Line->next;
		free (Line);
		Line = nextLine;
		}

	NumInputLinesPtr [0] = length;
	return MemoryOk;
	}


#include <string.h>


int WidthOk (int NumInputLines, char * * InputLines)
	{
	int i, width, MinWidth, MaxWidth;

	if (NumInputLines < 1) {
		fprintf (stderr, "***keytoppm: Empty input.\n");
		return 0;
		}
	width = strlen (InputLines [0] );
	MinWidth = width;
	MaxWidth = width;
	for (i = 0; i < NumInputLines; i++) {
		width = strlen (InputLines [i] );
		if (MinWidth > width)
			MinWidth = width;
		if (MaxWidth < width)
			MaxWidth = width;
		}
	if (MinWidth == MaxWidth) {
		fprintf (stderr, "keytoppm: Writing %d x %d.\n",
				MinWidth, NumInputLines);
		return 1;
		}
	fprintf (stderr, "***keytoppm: Input lines must be same length,");
	fprintf (stderr, " found %d to %d.\n", MinWidth, MaxWidth);
	return 0;
	}


void WriteImage (
		int NumInputLines,
		char * * InputLines,
		int NumKeys,
		char * * Keys)
	{
	int i, j, k, r, g, b, KeyFound;
	char c, key;

	printf ("P6\n");
	printf ("%d %d\n", (int) strlen (InputLines [0] ), NumInputLines);
	printf ("255\n");
	for (i = 0; i < NumInputLines; i++) {
		j = 0;
		c = InputLines [i] [j];
		while (c != '\0') {
			k = 0;
			KeyFound = 0;
			while (k < NumKeys && !KeyFound) {
				if (sscanf (Keys [k], "%c=%d,%d,%d",
						& key, & r, & g, & b) == 4
				&& c == key)
					KeyFound = 1;
				k++;
				} 
			if (KeyFound) {
				putchar (r);
				putchar (g);
				putchar (b);
				}
			else {
				putchar (DefaultR);
				putchar (DefaultG);
				putchar (DefaultB);
				}
			j++;
			c = InputLines [i] [j];
			}
		}
	}


int main (int argc, char * * argv)
	{
	char * * InputLines;
	int NumInputLines, i, r, g, b, ok;
	char c, x;

	ok = 0;
	if (argc < 2) {
		for (i = 0; i < NumUsageLines; i++)
			printf ("%s\n", UsageLines [i] );
		}
	else {
		ok = 1;
		for (i = 1; i < argc; i++) {
			if (sscanf (argv [i], "%c=%d,%d,%d%c",
					& c, & r, & g, & b, & x) != 4) {
				fprintf (stderr, "***keytoppm: Improper");
				fprintf (stderr, " \"%s\"", argv [i] );
				fprintf (stderr, " on command line.\n");
				ok = 0;
				}
			}
		}
	if (ok) {
		if (ReadInputLines (& NumInputLines, & InputLines)
		&& WidthOk (NumInputLines - 1, InputLines) )
			WriteImage (
				NumInputLines - 1,
				InputLines,
				argc - 1,
				argv + 1);
		for (i = 0; i < NumInputLines; i++)
			free (InputLines [i] );
		if (NumInputLines > 0)
			free (InputLines);
		}

	return 0;
	}