const char * UsageLines [] = {
       "Usage: p6paint (default r) (default g) (default b)",
       "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.",
       "The letter indicates the color for that pixel.  The default",
       "color specified on the command line will be used for",
       "any letter not in the list.",
       "",
       "May 20, 2020.  For latest, see gopher://sdf.org/1/users/julianbr",
       "(For the opposite of 'p6paint' see 'p6show').",
       "",
       "Compiled for the following colors (use lowercase letter):"
       "",
       };
const int NumUsageLines = sizeof (UsageLines) / sizeof (UsageLines [0] );


const struct {
       char * name;
       char letter;
       int r;
       int g;
       int b;
       } Colors [] = {
       {"rED", 'r', 255, 0, 0 },
       {"PiNK", 'i', 255, 192, 203 },
       {"oRANGE", 'o', 255, 127, 0 },
       {"yELLOW", 'y', 255, 255, 0 },
       {"gREEN", 'g', 0, 255, 0 },
       {"bLUE", 'b', 0, 0, 255 },
       {"pURPLE", 'p', 128, 0, 128 },
       {"wHITE", 'w', 255, 255, 255 },
       {"GRaY", 'a', 128, 128, 128 },
       {"tAN", 't', 210, 180, 140 },
       {"BROWn", 'n', 150, 75, 0 },
       {"BLACk", 'k', 0, 0, 0 },
       };
const int NumColors = sizeof (Colors) / sizeof (Colors [0] );


#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, "***p6paint: 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, "***p6paint: 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, "***p6paint: 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, "***p6paint: 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, "***p6paint: 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, "p6paint: Writing %d x %d.\n",
                               MinWidth, NumInputLines);
               return 1;
               }
       fprintf (stderr, "***p6paint: Input lines must be same length,");
       fprintf (stderr, " found %d to %d.\n", MinWidth, MaxWidth);
       return 0;
       }


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

       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') {
                       KeyFound = 0;
                       k = 0;
                       while (!KeyFound && k < NumColors) {
                               if (c == Colors [k].letter)
                                       KeyFound = 1;
                               else
                                       k++;
                               }
                       if (KeyFound) {
                               putchar (Colors [k].r);
                               putchar (Colors [k].g);
                               putchar (Colors [k].b);
                               }
                       else {
                               putchar (r);
                               putchar (g);
                               putchar (b);
                               }
                       j++;
                       c = InputLines [i] [j];
                       }
               }
       }


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

       ok = 0;
       if (argc != 4) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               for (i = 0; i < NumColors; i++)
                       printf ("%s ", Colors [i].name);
               printf ("\n");
               }
       else {
               ok = 1;
               if (sscanf (argv [1], "%d%c", & r, & c) != 1 || r < 0 || r > 255) {
                       fprintf (stderr, "***p6paint: Improper r \"%s\"", argv [1] );
                       fprintf (stderr, " - must be integer from 0 to 255\n");
                       ok = 0;
                       }
               if (sscanf (argv [2], "%d%c", & g, & c) != 1 || g < 0 || g > 255) {
                       fprintf (stderr, "***p6paint: Improper g \"%s\"", argv [2] );
                       fprintf (stderr, " - must be integer from 0 to 255\n");
                       ok = 0;
                       }
               if (sscanf (argv [3], "%d%c", & b, & c) != 1 || b < 0 || b > 255) {
                       fprintf (stderr, "***p6paint: Improper b \"%s\"", argv [3] );
                       fprintf (stderr, " - must be integer from 0 to 255\n");
                       ok = 0;
                       }
               }
       if (ok) {
               if (ReadInputLines (& NumInputLines, & InputLines)
               && WidthOk (NumInputLines - 1, InputLines) )
                       WriteImage (
                               NumInputLines - 1,
                               InputLines,
                               r,
                               g,
                               b
                               );
               for (i = 0; i < NumInputLines; i++)
                       free (InputLines [i] );
               if (NumInputLines > 0)
                       free (InputLines);
               }

       return 0;
       }