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;
       }