about summary refs log tree commit diff
path: root/converter/ppm/xpmtoppm.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/ppm/xpmtoppm.c')
-rw-r--r--converter/ppm/xpmtoppm.c1149
1 files changed, 698 insertions, 451 deletions
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c
index 235c3867..27f17931 100644
--- a/converter/ppm/xpmtoppm.c
+++ b/converter/ppm/xpmtoppm.c
@@ -1,43 +1,19 @@
-/* xpmtoppm.c - read an X11 pixmap file and produce a portable pixmap
-**
-** Copyright (C) 1991 by Jef Poskanzer.
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-**
-** Upgraded to handle XPM version 3 by
-**   Arnaud Le Hors (lehors@mirsa.inria.fr)
-**   Tue Apr 9 1991
-**
-** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
-**  - Bug fix, no advance of read ptr, would not read 
-**    colors like "ac c black" because it would find 
-**    the "c" of "ac" and then had problems with "c"
-**    as color.
-**    
-**  - Now understands multiword X11 color names
-**  
-**  - Now reads multiple color keys. Takes the color
-**    of the hightest available key. Lines no longer need
-**    to begin with key 'c'.
-**    
-**  - expanded line buffer to from 500 to 2048 for bigger files
+/* xpmtoppm.c - convert XPM file (X11 pixmap) to PPM
+
+   Copyright and history information is at end of file
 */
 
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
-#include "mallocvar.h"
+#include "ppm.h"
 
 #define MAX_LINE (8 * 1024)
   /* The maximum size XPM input line we can handle. */
@@ -47,72 +23,321 @@
 
 const char *xpmColorKeys[] =
 {
- "s",					/* key #1: symbol */
- "m",					/* key #2: mono visual */
- "g4",					/* key #3: 4 grays visual */
- "g",					/* key #4: gray visual */
- "c",					/* key #5: color visual */
+ "s",                   /* key #1: symbol */
+ "m",                   /* key #2: mono visual */
+ "g4",                  /* key #3: 4 grays visual */
+ "g",                   /* key #4: gray visual */
+ "c",                   /* key #5: color visual */
 };
 
-struct cmdline_info {
+struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filespec;  /* Filespecs of input files */
-    char *alpha_filename;
+    const char * input_filespec;  /* Filespecs of input files */
+    const char * alpha_filename;
     int alpha_stdout;
     int verbose;
 };
 
 
-static int verbose;
+static bool verbose;
+
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
 
     unsigned int option_def_index;
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "alphaout",   OPT_STRING, &cmdline_p->alpha_filename, 0);
-    OPTENTRY(0,   "verbose",    OPT_FLAG,   &cmdline_p->verbose,        0);
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "alphaout",   OPT_STRING, &cmdlineP->alpha_filename,
+            NULL, 0);
+    OPTENT3(0,   "verbose",    OPT_FLAG,   &cmdlineP->verbose,
+            NULL, 0);
 
-    cmdline_p->alpha_filename = NULL;
-    cmdline_p->verbose = FALSE;
+    cmdlineP->alpha_filename = NULL;
+    cmdlineP->verbose = FALSE;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
 
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc - 1 == 0)
-        cmdline_p->input_filespec = NULL;  /* he wants stdin */
+        cmdlineP->input_filespec = NULL;  /* he wants stdin */
     else if (argc - 1 == 1)
-        cmdline_p->input_filespec = strdup(argv[1]);
+        cmdlineP->input_filespec = strdup(argv[1]);
     else 
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
 
-    if (cmdline_p->alpha_filename && 
-        streq(cmdline_p->alpha_filename, "-"))
-        cmdline_p->alpha_stdout = TRUE;
+    if (cmdlineP->alpha_filename && 
+        streq(cmdlineP->alpha_filename, "-"))
+        cmdlineP->alpha_stdout = TRUE;
     else 
-        cmdline_p->alpha_stdout = FALSE;
+        cmdlineP->alpha_stdout = FALSE;
+
+}
+
+
+
+struct ColorNameHashTableEntry {
+/*----------------------------------------------------------------------------
+   An entry in the color name hash table.  It maps a color name to a
+   color, or is empty.
+-----------------------------------------------------------------------------*/
+    bool empty;
+    char colorName[3];
+        /* Actual length 0-3.  NOT NUL-terminated */
+    pixel color;
+};
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a color map which is primarily a hash table that maps an XPM
+   color name to a color.  An XPM color name is a 0-3 character name that
+   appears in the raster of an XPM image to uniquely identify a color.
+   The header of the XPM contains a listing of all the color names that
+   appear in the raster, identifying a color for each.
+
+   We represent a color as a 'pixel'.
+-----------------------------------------------------------------------------*/
+    unsigned int nameSize;
+        /* Size of color names in this hash.  0-3 */
+    struct ColorNameHashTableEntry * transparentP;
+        /* The element of 'table' that is for the transparent color.
+           NULL if there is none.
+        */
+
+    /* This is an internally chained hash table, i.e. there are no linked
+       lists.  You use the hash function to get an index into the hash table.
+       If the entry indexed by that is not for the color name you're looking
+       for, you look at the next entry down, and keep going down until you
+       either find the color name you're looking for or hit an empty entry.
+
+       So that we never run out of space for new color names, we make the
+       creator of the hash table tell us the maximum number of colors there
+       will be.  We allocate twice that size in order to reduce average hash
+       chain length.
+    */
+    unsigned int size;
+    struct ColorNameHashTableEntry * table;
+} ColorNameHash;
+
+
+
+static ColorNameHash *
+hash_create(unsigned int const nColors,
+            unsigned int const nameSize) {
+
+    ColorNameHash * hashP;
+
+    MALLOCVAR_NOFAIL(hashP);
+
+    hashP->nameSize = nameSize;
+
+    hashP->size = nColors * 2;
+
+    MALLOCARRAY(hashP->table, hashP->size);
+
+    if (!hashP->table)
+        pm_error("Failed to allocate memory for a %u-entry "
+                 "color name hash table.", hashP->size);
+
+    {
+        unsigned int i;
+        for (i = 0; i < hashP->size; ++i)
+            hashP->table[i].empty = true;
+    }
+
+    hashP->transparentP = NULL;
+
+    return hashP;
+}
+
+
+
+static void
+hash_destroy(ColorNameHash * const hashP) {
+
+    free(hashP->table);
+
+    free(hashP);
+}
+
+
+
+static unsigned int
+hashColorName(const char * const name,
+              unsigned int const size,
+              unsigned int const hashTableSize) {
+/*----------------------------------------------------------------------------
+   Return the hash value (initial index into the color name hash table)
+   for the color name 'name', which is 'size' characters long.  The hash
+   is to be in the range [0, hashTableSize).
+-----------------------------------------------------------------------------*/
+    /* I have no idea if this is an appropriate hash function.  I borrowed
+       it from pnm_hashTuple()
+    */
+
+    unsigned int const hash_factor[] = {1, 33, 33*33};
+
+    unsigned int i;
+    unsigned int hash;
 
+    hash = 0;  /* initial value */
+    for (i = 0; i < size; ++i) {
+        hash += name[i] * hash_factor[i];
+    }
+    hash %= hashTableSize;
+    return hash;
+}
+
+
+
+static bool
+entryMatch(struct ColorNameHashTableEntry const entry,
+           const char *                   const name,
+           unsigned int                   const size) {
+
+    if (entry.empty)
+        return true;
+
+    assert(size <= ARRAY_SIZE(entry.colorName));
+
+    {
+        unsigned int i;
+        
+        for (i = 0; i < size; ++i) {
+            if (name[i] != entry.colorName[i])
+                return false;
+        }
+    }
+
+    return true;
+}
+
+
+
+static void
+bumpIndex(unsigned int * const indexP,
+          unsigned int   const tableSize,
+          unsigned int   const limit) {
+/*----------------------------------------------------------------------------
+   Bump *indexP to the next entry in a table of size 'tableSize', in a
+   circular fashion.  But abort the program if this would take us to
+   'limit'.
+-----------------------------------------------------------------------------*/
+    *indexP += 1;
+    if (*indexP >= tableSize)
+        *indexP = 0;
+
+    if (*indexP == limit)
+        pm_error("INTERNAL ERROR: color name hash table is full");
+}
+
+
+
+static void
+hash_find(const ColorNameHash *             const hashP,
+          const char *                      const name,
+          struct ColorNameHashTableEntry ** const entryPP) {
+/*----------------------------------------------------------------------------
+   Find the entry in the color hash table *hashP for the color
+   named 'name' in the lexicon of this XPM file.  If the color is in the
+   table, this is where it is.  If it isn't, this is where it should go.
+-----------------------------------------------------------------------------*/
+    unsigned int const initialIndex  =
+        hashColorName(name, hashP->nameSize, hashP->size);
+
+    unsigned int i;
+
+    for (i = initialIndex;
+         !entryMatch(hashP->table[i], name, hashP->nameSize);
+         bumpIndex(&i, hashP->size, initialIndex));
+         
+    *entryPP = &hashP->table[i];
 }
 
 
+
+static void
+hash_add(ColorNameHash * const hashP,
+         const char *    const name,
+         pixel           const color,
+         bool            const isTransparent) {
+
+    struct ColorNameHashTableEntry * entryP;
+
+    hash_find(hashP, name, &entryP);
+
+    if (!entryP->empty)
+        pm_error("Color name appears multiple times in color map");
+
+    entryP->empty = false;
+    {
+        unsigned int i;
+        for (i = 0; i < hashP->nameSize; ++i)
+            entryP->colorName[i] = name[i];
+    }
+    entryP->color = color;
+
+    if (isTransparent) {
+        if (hashP->transparentP)
+            pm_error("There are multiple NONE (transparent) entries in "
+                     "the XPM color map");
+        else
+            hashP->transparentP = entryP;
+    }
+}
+
+
+
+static pixel
+hash_color(const ColorNameHash * const hashP,
+           const char *          const name) {
+
+    struct ColorNameHashTableEntry * entryP;
+
+    hash_find(hashP, name, &entryP);
+
+    if (entryP->empty)
+        pm_error("Color name in raster is not in color map");
+
+    return entryP->color;
+}
+
+
+
+static bool
+hash_isTransparent(const ColorNameHash * const hashP,
+                   const char *          const name) {
+
+    struct ColorNameHashTableEntry * entryP;
+
+    hash_find(hashP, name, &entryP);
+
+    return (entryP == hashP->transparentP);
+}
+
+
+
 static char lastInputLine[MAX_LINE+1];
     /* contents of line most recently read from input */
 static bool backup;
@@ -121,6 +346,7 @@ static bool backup;
     */
 
 
+
 static void
 getLine(char * const line,
         size_t const size,
@@ -158,21 +384,6 @@ getLine(char * const line,
 
 
 
-static unsigned int
-getNumber(char * const p, unsigned int const size) {
-
-    unsigned int retval;
-    unsigned char * q;
-    
-    retval = 0;
-    for (q = p; q < p+size; ++q)
-        retval = (retval << 8) + *q;
-
-    return retval;
-}
-
-
-
 static void
 getword(char * const output, char ** const cursorP) {
 
@@ -191,90 +402,97 @@ getword(char * const output, char ** const cursorP) {
 
 
 static void
-addToColorMap(unsigned int const seqNum,
-              unsigned int const colorNumber, 
-              pixel * const colors, int * const ptab, 
-              char colorspec[], int const isTransparent,
-              int * const transparentP) {
+addToColorMap(ColorNameHash * const hashP,
+              const char *    const colorName,
+              char            const colorspec[],
+              bool            const isTransparent) {
 /*----------------------------------------------------------------------------
-   Add the color named by colorspec[] to the colormap contained in
-   'colors' and 'ptab', as the color associated with XPM color number
-   'colorNumber', which is the seqNum'th color in the XPM color map.
+   Add the color named by colorspec[] to the colormap represented by *hashP,
+   as the color associated with XPM color name 'colorNumber'.
 
-   Iff 'transparent', set *transparentP to the colormap index that 
-   corresponds to this color.
+   Note that *hashP determines how long 'colorName' is.
 -----------------------------------------------------------------------------*/
-    if (ptab == NULL) {
-        /* Index into table. */
-        colors[colorNumber] = ppm_parsecolor(colorspec,
-                                             (pixval) PPM_MAXMAXVAL);
-        if (isTransparent) 
-            *transparentP = colorNumber;
-    } else {
-        /* Set up linear search table. */
-        colors[seqNum] = ppm_parsecolor(colorspec,
-                                        (pixval) PPM_MAXMAXVAL);
-        ptab[seqNum] = colorNumber;
-        if (isTransparent)
-            *transparentP = seqNum;
+    hash_add(hashP, colorName, ppm_parsecolor(colorspec, PPM_MAXMAXVAL),
+             isTransparent);
+}
+
+
+
+static void
+validateColorName(const char * const name,
+                  unsigned int const charsPerPixel) {
+
+    unsigned int i;
+
+    for (i = 0; i < charsPerPixel; ++i) {
+        if (name[i] == '"')
+            pm_error("A color map entry ends in the middle of the colormap "
+                     "index");
+        else if (name[i] == '\0')
+            pm_error("The XPM file ends in the middle of a color map entry");
     }
 }
 
 
 
 static void
-interpretXpm3ColorTableLine(char line[], int const seqNum, 
-                            int const chars_per_pixel,
-                            pixel * const colors, int * const ptab,
-                            int * const transparentP) {
+interpretXpm3ColorTableLine(char               const line[],
+                            unsigned int       const seqNum, 
+                            unsigned int       const charsPerPixel,
+                            ColorNameHash *    const hashP) {
 /*----------------------------------------------------------------------------
-   Interpret one line of the color table in the XPM header.  'line' is
-   the line from the XPM file.  It is the seqNum'th color table entry in
-   the file.  The file uses 'chars_per_pixel' characters per pixel.
+   Interpret one line of the color table in the XPM header.  'line' is the
+   line from the XPM file.  It is the seqNum'th color table entry in the file.
+   The raster in the file uses 'charsPerPixel' characters per pixel (i.e.
+   a an XPM color name is 'charsPerPixel' characters).
 
-   Add the information from this color table entry to the color table
-   'colors' and, if it isn't NULL, the corresponding lookup shadow table
-   'ptab' (see readXpm3ColorTable for a description of these data 
-   structures).
+   Add the information from this color table entry to the color name hash
+   *hashP.
 
    The line may include values for multiple kinds of color (grayscale,
    color, etc.).  We take the highest of these (e.g. color over grayscale).
 
    If a color table entry indicates transparency, set *transparentP
-   to the colormap index that corresponds to the indicated color.
+   to indicate the XPM color name.
 -----------------------------------------------------------------------------*/
     /* Note: this code seems to allow for multi-word color specifications,
        but I'm not aware that such are legal.  Ultimately, ppm_parsecolor()
-       interprets the name, and I believe it only takes single word 
+       interprets the name, and I believe it takes only single word 
        color specifications.  -Bryan 2001.05.06.
     */
     char str2[MAX_LINE+1];    
-    char *t1;
-    char *t2;
+    char * t1;
+    char * t2;
     int endOfEntry;   /* boolean */
     
-    unsigned int curkey, key, highkey;	/* current color key */
-    unsigned int lastwaskey;	
+    unsigned int curkey, key, highkey;  /* current color key */
+    bool lastwaskey;    
         /* The last token we processes was a key, and we have processed
            at least one token.
         */
-    char curbuf[BUFSIZ];		/* current buffer */
-    int isTransparent;
+    char curbuf[BUFSIZ];        /* current buffer */
+    bool isTransparent;
     
-    int colorNumber;
-        /* A color number that appears in the raster */
+    const char * colorName;
+        /* The 0-3 character name this color map line gives the color
+           (i.e. the name that the raster uses).  This is NOT NUL-terminated.
+           It's length is bytesPerPixel.
+        */
+
     /* read the chars */
     t1 = strchr(line, '"');
     if (t1 == NULL)
         pm_error("A line that is supposed to be an entry in the color "
                  "table does not start with a quote.  The line is '%s'.  "
-                 "It is the %dth entry in the color table.", 
+                 "It is the %uth entry in the color table.", 
                  line, seqNum);
     else
-        t1++;  /* Points now to first color number character */
+        ++t1;  /* Points now to first color number character */
+    
+    validateColorName(t1, charsPerPixel);
+    colorName = t1;
 
-    colorNumber = getNumber(t1, chars_per_pixel);
-    t1 += chars_per_pixel;
+    t1 += charsPerPixel;
 
     /*
      * read color keys and values 
@@ -306,30 +524,30 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
                     pm_error("Missing color key token in color table line "
                              "'%s' before '%s'.", line, str2);
                 if (!lastwaskey) 
-                    strcat(curbuf, " ");		/* append space */
-                if ( (strncmp(str2, "None", 4) == 0) 
-                     || (strncmp(str2, "none", 4) == 0) ) {
+                    strcat(curbuf, " ");        /* append space */
+                if ( (strneq(str2, "None", 4)) 
+                     || (strneq(str2, "none", 4)) ) {
                     /* This entry identifies the transparent color number */
                     strcat(curbuf, "#000000");  /* Make it black */
                     isTransparent = TRUE;
                 } else 
-                    strcat(curbuf, str2);		/* append buf */
-                lastwaskey = 0;
+                    strcat(curbuf, str2);       /* append buf */
+                lastwaskey = FALSE;
             } else { 
                 /* This word is a key.  So we've seen the last of the 
                    info for the previous key, and we must either put it
                    in the color map or ignore it if we already have a higher
                    color form in the colormap for this colormap entry.
                 */
-                if (curkey > highkey) {	/* flush string */
-                    addToColorMap(seqNum, colorNumber, colors, ptab, curbuf,
-                                  isTransparent, transparentP);
+                if (curkey > highkey) { /* flush string */
+                    addToColorMap(hashP, colorName, curbuf, isTransparent);
                     highkey = curkey;
                 }
-                curkey = key;			/* set new key  */
-                curbuf[0] = '\0';		/* reset curbuf */
+                /* intialize state to process this new key */
+                curkey = key;
+                curbuf[0] = '\0';
                 isTransparent = FALSE;
-                lastwaskey = 1;
+                lastwaskey = TRUE;
             }
             if (*t2 == '"') break;
         }
@@ -339,8 +557,7 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
        entry in it)
     */
     if (curkey > highkey) {
-        addToColorMap(seqNum, colorNumber, colors, ptab, curbuf,
-                      isTransparent, transparentP);
+        addToColorMap(hashP, colorName, curbuf, isTransparent);
         highkey = curkey;
     }
     if (highkey == 1) 
@@ -350,152 +567,208 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
 
 
 static void
-readXpm3Header(FILE * const stream, int * const widthP, int * const heightP, 
-               int * const chars_per_pixelP, int * const ncolorsP,
-               pixel ** const colorsP, int ** const ptabP,
-               int * const transparentP) {
+readV3ColorTable(FILE *             const ifP,
+                 ColorNameHash **   const colorNameHashPP,
+                 unsigned int       const nColors,
+                 unsigned int       const charsPerPixel) {
 /*----------------------------------------------------------------------------
-  Read the header of the XPM file on stream 'stream'.  Assume the
+   Read the color table from the XPM Version 3 header.
+
+   Assume *ifP is positioned to the color table; leave it positioned after.
+-----------------------------------------------------------------------------*/
+    ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel);
+
+    unsigned int seqNum;
+        /* Sequence number of entry within color table in XPM header */
+
+    for (seqNum = 0; seqNum < nColors; ++seqNum) {
+        char line[MAX_LINE+1];
+        getLine(line, sizeof(line), ifP);
+        /* skip the comment line if any */
+        if (strneq(line, "/*", 2))
+            getLine(line, sizeof(line), ifP);
+            
+        interpretXpm3ColorTableLine(line, seqNum, charsPerPixel,
+                                    colorNameHashP);
+                                    
+    }
+    *colorNameHashPP = colorNameHashP;
+}
+
+
+
+static void
+readXpm3Header(FILE *             const ifP,
+               unsigned int *     const widthP,
+               unsigned int *     const heightP, 
+               unsigned int *     const charsPerPixelP,
+               ColorNameHash **   const colorNameHashPP) {
+/*----------------------------------------------------------------------------
+  Read the header of the XPM file on stream *ifP.  Assume the
   getLine() stream is presently positioned to the beginning of the
   file and it is a Version 3 XPM file.  Leave the stream positioned
   after the header.
 
-  We have two ways to return the colormap, depending on the number of
-  characters per pixel in the XPM:  
-  
-  If it is 1 or 2 characters per pixel, we return the colormap as a
-  Netpbm 'pixel' array *colorsP (in newly malloc'ed storage), such
-  that if a color in the raster is identified by index N, then
-  (*colorsP)[N] is that color.  So this array is either 256 or 64K
-  pixels.  In this case, we return *ptabP = NULL.
-
-  If it is more than 2 characters per pixel, we return the colormap as
-  both a Netpbm 'pixel' array *colorsP and a lookup table *ptabP (both
-  in newly malloc'ed storage).
-
-  If a color in the raster is identified by index N, then for some I,
-  (*ptabP)[I] is N and (*colorsP)[I] is the color in question.  So 
-  you iterate through *ptabP looking for N and then look at the 
-  corresponding entry in *colorsP to get the color.
-
-  Return as *transColorNumberP the value of the XPM color number that
-  represents a transparent pixel, or -1 if no color number does.
+  Return as *widthP and *heightP the dimensions of the image indicated
+  by the header.
+
+  Return as *charsPerPixelP the number of characters the header says the
+  raster uses for each pixel, i.e. the XPM color name length.
+
+  Return the color map as *colorNameHashPP.
 -----------------------------------------------------------------------------*/
     char line[MAX_LINE+1];
     const char * xpm3_signature = "/* XPM */";
     
-    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1;
+    unsigned int width, height;
+    unsigned int nColors;
+    unsigned int charsPerPixel;
 
     /* Read the XPM signature comment */
-    getLine(line, sizeof(line), stream);
-    if (strncmp(line, xpm3_signature, strlen(xpm3_signature)) != 0) 
+    getLine(line, sizeof(line), ifP);
+    if (!strneq(line, xpm3_signature, strlen(xpm3_signature))) 
         pm_error("Apparent XPM 3 file does not start with '/* XPM */'.  "
                  "First line is '%s'", xpm3_signature);
 
     /* Read the assignment line */
-    getLine(line, sizeof(line), stream);
-    if (strncmp(line, "static char", 11) != 0)
+    getLine(line, sizeof(line), ifP);
+    if (!strneq(line, "static char", 11))
         pm_error("Cannot find data structure declaration.  Expected a "
                  "line starting with 'static char', but found the line "
                  "'%s'.", line);
 
-	/* Read the hints line */
-    getLine(line, sizeof(line), stream);
-    /* skip the comment line if any */
-    if (!strncmp(line, "/*", 2)) {
+    getLine(line, sizeof(line), ifP);
+
+    /* Skip the comment block, if one starts here */
+    if (strneq(line, "/*", 2)) {
         while (!strstr(line, "*/"))
-            getLine(line, sizeof(line), stream);
-        getLine(line, sizeof(line), stream);
+            getLine(line, sizeof(line), ifP);
+        getLine(line, sizeof(line), ifP);
     }
-    if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP,
-               ncolorsP, chars_per_pixelP) != 4)
+
+    /* Parse the hints line */
+    if (sscanf(line, "\"%u %u %u %u\",", &width, &height,
+               &nColors, &charsPerPixel) != 4)
         pm_error("error scanning hints line");
 
-    if (verbose == 1) 
-    {
-        pm_message("Width x Height:  %d x %d", *widthP, *heightP);
-        pm_message("no. of colors:  %d", *ncolorsP);
-        pm_message("chars per pixel:  %d", *chars_per_pixelP);
+    if (verbose) {
+        pm_message("Width x Height:  %u x %u", width, height);
+        pm_message("no. of colors:  %u", nColors);
+        pm_message("chars per pixel:  %u", charsPerPixel);
     }
 
-    /* Allocate space for color table. */
-    if (*chars_per_pixelP <= 2) {
-        /* Set up direct index (see above) */
-        *colorsP = ppm_allocrow(*chars_per_pixelP == 1 ? 256 : 256*256);
-        *ptabP = NULL;
-    } else {
-        /* Set up lookup table (see above) */
-        *colorsP = ppm_allocrow(*ncolorsP);
-        MALLOCARRAY(*ptabP, *ncolorsP);
-        if (*ptabP == NULL)
-            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
-    }
-    
-    { 
-        /* Read the color table */
-        int seqNum;
-            /* Sequence number of entry within color table in XPM header */
-
-        *transparentP = -1;  /* initial value */
-
-        for (seqNum = 0; seqNum < *ncolorsP; seqNum++) {
-            getLine(line, sizeof(line), stream);
-            /* skip the comment line if any */
-            if (!strncmp(line, "/*", 2))
-                getLine(line, sizeof(line), stream);
-            
-            interpretXpm3ColorTableLine(line, seqNum, *chars_per_pixelP, 
-                                        *colorsP, *ptabP, transparentP);
-        }
+    readV3ColorTable(ifP, colorNameHashPP, nColors, charsPerPixel);
+
+    *widthP         = width;
+    *heightP        = height;
+    *charsPerPixelP = charsPerPixel;
+}
+
+
+
+static void
+readV1ColorTable(FILE *           const ifP,
+                 ColorNameHash ** const colorNameHashPP,
+                 unsigned int     const nColors,
+                 unsigned int     const charsPerPixel) {
+/*----------------------------------------------------------------------------
+   Read the color table from the XPM Version 1 header.
+
+   Assume *ifP is positioned to the color table; leave it positioned after.
+-----------------------------------------------------------------------------*/
+    ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel);
+
+    unsigned int i;
+
+    for (i = 0; i < nColors; ++i) {
+        char line[MAX_LINE+1];
+        char str1[MAX_LINE+1];
+        char str2[MAX_LINE+1];
+        char * t1;
+        char * t2;
+
+        getLine(line, sizeof(line), ifP);
+
+        if ((t1 = strchr(line, '"')) == NULL)
+            pm_error("D error scanning color table");
+        if ((t2 = strchr(t1 + 1, '"')) == NULL)
+            pm_error("E error scanning color table");
+        if (t2 - t1 - 1 != charsPerPixel)
+            pm_error("wrong number of chars per pixel in color table");
+        strncpy(str1, t1 + 1, t2 - t1 - 1);
+        str1[t2 - t1 - 1] = '\0';
+
+        if ((t1 = strchr(t2 + 1, '"')) == NULL)
+            pm_error("F error scanning color table");
+        if ((t2 = strchr(t1 + 1, '"')) == NULL)
+            pm_error("G error scanning color table");
+        strncpy(str2, t1 + 1, t2 - t1 - 1);
+        str2[t2 - t1 - 1] = '\0';
+
+        addToColorMap(colorNameHashP, str1, str2, false);
     }
+    *colorNameHashPP = colorNameHashP;
 }
 
 
+
 static void
-readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, 
-               int * const chars_per_pixelP, int * const ncolorsP, 
-               pixel ** const colorsP, int ** const ptabP) {
+readXpm1Header(FILE *           const ifP,
+               unsigned int *   const widthP,
+               unsigned int *   const heightP, 
+               unsigned int *   const charsPerPixelP,
+               ColorNameHash ** const colorNameHashPP) {
 /*----------------------------------------------------------------------------
-  Read the header of the XPM file on stream 'stream'.  Assume the
+  Read the header of the XPM file on stream *ifP.  Assume the
   getLine() stream is presently positioned to the beginning of the
   file and it is a Version 1 XPM file.  Leave the stream positioned
   after the header.
   
   Return the information from the header the same as for readXpm3Header.
 -----------------------------------------------------------------------------*/
-    char line[MAX_LINE+1], str1[MAX_LINE+1], str2[MAX_LINE+1];
-    char *t1;
-    char *t2;
-    int format;
-    unsigned int v;
-    int i, j;
+    int format, v;
     bool processedStaticChar;  
         /* We have read up to and interpreted the "static char..." line */
+    char * t1;
+    unsigned int nColors;
+    bool gotPixel, gotNColors, gotWidth, gotHeight, gotFormat;
 
-    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1;
+    gotNColors = false;
+    gotWidth   = false;
+    gotHeight  = false;
+    gotFormat  = false;
+    gotPixel   = false;
 
     /* Read the initial defines. */
     processedStaticChar = FALSE;
     while (!processedStaticChar) {
-        getLine(line, sizeof(line), stream);
+        char line[MAX_LINE+1];
+        char str1[MAX_LINE+1];
+
+        getLine(line, sizeof(line), ifP);
 
         if (sscanf(line, "#define %s %d", str1, &v) == 2) {
-            char *t1;
             if ((t1 = strrchr(str1, '_')) == NULL)
                 t1 = str1;
             else
                 ++t1;
-            if (streq(t1, "format"))
+            if (streq(t1, "format")) {
+                gotFormat = true;
                 format = v;
-            else if (streq(t1, "width"))
+            } else if (streq(t1, "width")) {
+                gotWidth = true;
                 *widthP = v;
-            else if (streq(t1, "height"))
+            } else if (streq(t1, "height")) {
+                gotHeight = true;
                 *heightP = v;
-            else if (streq(t1, "ncolors"))
-                *ncolorsP = v;
-            else if (streq(t1, "pixel"))
-                *chars_per_pixelP = v;
-        } else if (!strncmp(line, "static char", 11)) {
+            } else if (streq(t1, "nColors")) {
+                gotNColors = true;
+                nColors = v;
+            } else if (streq(t1, "pixel")) {
+                gotPixel = TRUE;
+                *charsPerPixelP = v;
+            }
+        } else if (strneq(line, "static char", 11)) {
             if ((t1 = strrchr(line, '_')) == NULL)
                 t1 = line;
             else
@@ -507,85 +780,40 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
        t1 points to position of last "_" in the line, or the beginning of
        the line if there is no "_"
     */
-    if (format == -1)
+    if (!gotPixel)
+        pm_error("No 'pixel' value (characters per pixel)");
+    if (!gotFormat)
         pm_error("missing or invalid format");
     if (format != 1)
         pm_error("can't handle XPM version %d", format);
-    if (*widthP == -1)
+    if (!gotWidth)
         pm_error("missing or invalid width");
-    if (*heightP == -1)
+    if (!gotHeight)
         pm_error("missing or invalid height");
-    if (*ncolorsP == -1)
-        pm_error("missing or invalid ncolors");
-    if (*chars_per_pixelP == -1)
-        pm_error("missing or invalid *chars_per_pixelP");
-    if (*chars_per_pixelP > 2)
-        pm_message("WARNING: *chars_per_pixelP > 2 uses a lot of memory");
+    if (!gotNColors)
+        pm_error("missing or invalid nColors");
+
+    if (*charsPerPixelP > 2)
+        pm_message("WARNING: > 2 characters per pixel uses a lot of memory");
 
     /* If there's a monochrome color table, skip it. */
-    if (!strncmp(t1, "mono", 4)) {
+    if (strneq(t1, "mono", 4)) {
         for (;;) {
-            getLine(line, sizeof(line), stream);
-            if (!strncmp(line, "static char", 11))
+            char line[MAX_LINE+1];
+            getLine(line, sizeof(line), ifP);
+            if (strneq(line, "static char", 11))
                 break;
         }
     }
-    /* Allocate space for color table. */
-    if (*chars_per_pixelP <= 2) {
-        /* Up to two chars per pixel, we can use an indexed table. */
-        v = 1;
-        for (i = 0; i < *chars_per_pixelP; ++i)
-            v *= 256;
-        *colorsP = ppm_allocrow(v);
-        *ptabP = NULL;
-    } else {
-        /* Over two chars per pixel, we fall back on linear search. */
-        *colorsP = ppm_allocrow(*ncolorsP);
-        MALLOCARRAY(*ptabP, *ncolorsP);
-        if (*ptabP == NULL)
-            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
-    }
-
-    /* Read color table. */
-    for (i = 0; i < *ncolorsP; ++i) {
-        getLine(line, sizeof(line), stream);
+    readV1ColorTable(ifP, colorNameHashPP, nColors, *charsPerPixelP);
 
-        if ((t1 = strchr(line, '"')) == NULL)
-            pm_error("D error scanning color table");
-        if ((t2 = strchr(t1 + 1, '"')) == NULL)
-            pm_error("E error scanning color table");
-        if (t2 - t1 - 1 != *chars_per_pixelP)
-            pm_error("wrong number of chars per pixel in color table");
-        strncpy(str1, t1 + 1, t2 - t1 - 1);
-        str1[t2 - t1 - 1] = '\0';
-
-        if ((t1 = strchr(t2 + 1, '"')) == NULL)
-            pm_error("F error scanning color table");
-        if ((t2 = strchr(t1 + 1, '"')) == NULL)
-            pm_error("G error scanning color table");
-        strncpy(str2, t1 + 1, t2 - t1 - 1);
-        str2[t2 - t1 - 1] = '\0';
-
-        v = 0;
-        for (j = 0; j < *chars_per_pixelP; ++j)
-            v = (v << 8) + str1[j];
-        if (*chars_per_pixelP <= 2)
-            /* Index into table. */
-            (*colorsP)[v] = ppm_parsecolor(str2,
-                                           (pixval) PPM_MAXMAXVAL);
-        else {
-            /* Set up linear search table. */
-            (*colorsP)[i] = ppm_parsecolor(str2,
-                                           (pixval) PPM_MAXMAXVAL);
-            (*ptabP)[i] = v;
-        }
-    }
     /* Position to first line of raster (which is the line after
        "static char ...").
     */
     for (;;) {
-        getLine(line, sizeof(line), stream);
-        if (strncmp(line, "static char", 11) == 0)
+        char line[MAX_LINE+1];
+        getLine(line, sizeof(line), ifP);
+        if (strneq(line, "static char", 11))
             break;
     }
 }
@@ -593,29 +821,49 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
 
 
 static void
-interpretXpmLine(char   const line[],
-                 int    const chars_per_pixel,
-                 int    const ncolors,
-                 int *  const ptab, 
-                 int ** const cursorP,
-                 int *  const maxCursor) {
+validateRasterPixel(const char * const pixelChars,
+                    unsigned int const charsPerPixel) {
+
+    unsigned int i;
+
+    for (i = 0; i < charsPerPixel; ++i) {
+        if (pixelChars[i] == '\0')
+            pm_error("XPM input file ends in the middle of a string "
+                     "that represents a raster line");
+        else if (pixelChars[i] == '"')
+            pm_error("A string that represents a raster line in the "
+                     "XPM input file is too short to contain all the "
+                     "pixels (%u characters each)",
+                     charsPerPixel);
+    }
+}
+
+
+
+static void
+convertRow(char                  const line[],
+           unsigned int          const width,
+           unsigned int          const charsPerPixel,
+           const ColorNameHash * const colorNameHashP,
+           pixel *               const pixrow,
+           bit *                 const alpharow) {
 /*----------------------------------------------------------------------------
-   Interpret one line of XPM input.  The line is in 'line', and its
-   format is 'chars_per_pixel' characters per pixel.  'ptab' is the
-   color table that applies to the line, which table has 'ncolors'
-   colors.
+   Convert one row from XPM input, which describes one raster line of the
+   image, to PPM.  The XPM line is in 'line', and its format is 'width' pixel,
+   'charsPerPixel' characters per pixel.  *colorNameHashP is the color table
+   that applies to the line.
+
+   Put the PPM pixels in 'pixrow'.
 
-   Put the colormap indexes for the pixels represented in 'line' at
-   *cursorP, lined up in the order they are in 'line', and return
-   *cursorP positioned just after the last one.
+   Also produce PBM row 'alpharow' with the transparency information from the
+   row.
 
    If the line doesn't start with a quote (e.g. it is empty), we issue
    a warning and just treat the line as one that describes no pixels.
 
-   Stop processing the line either at the end of the line or when
-   the output would go beyond maxCursor, whichever comes first.
+   Abort program if there aren't exactly 'width' pixels in the line.
 -----------------------------------------------------------------------------*/
-    char * lineCursor;
+    const char * lineCursor;
 
     lineCursor = strchr(line, '"');  /* position to 1st quote in line */
     if (lineCursor == NULL) {
@@ -626,209 +874,208 @@ interpretXpmLine(char   const line[],
                    "line which is supposed to be a line of raster data: "
                    "'%s'.  Ignoring this line.", line);
     } else {
+        unsigned int col;
+    
         ++lineCursor; /* Skip to first character after quote */
 
         /* Handle pixels until a close quote, eol, or we've returned all
            the pixels Caller wants.
         */
-        while (*lineCursor && *lineCursor != '"' && *cursorP <= maxCursor) {
-            int colorNumber;
-            int i;
-            colorNumber = 0;  /* initial value */
-            for (i = 0; i < chars_per_pixel; ++i)
-                colorNumber = (colorNumber << 8) + *(lineCursor++);
-            if (ptab == NULL)
-                /* colormap is indexed directly by XPM color number */
-                *(*cursorP)++ = colorNumber;
-            else {
-                /* colormap shadows ptab[].  Find this color # in ptab[] */
-                int i;
-                for (i = 0; i < ncolors && ptab[i] != colorNumber; ++i);
-                if (i < ncolors)
-                    *(*cursorP)++ = i;
-                else
-                    pm_error("Color number %d is in raster, but not in "
-                             "colormap.  Line it's in: '%s'",
-                             colorNumber, line);
-            }
+        for (col = 0; col < width; ++col) {
+
+            validateRasterPixel(lineCursor, charsPerPixel);
+
+            pixrow[col] = hash_color(colorNameHashP, lineCursor);
+
+            alpharow[col] = hash_isTransparent(colorNameHashP, lineCursor) ?
+                PBM_BLACK : PBM_WHITE;
+            
+            lineCursor += charsPerPixel;
         }
+        if (*lineCursor != '"')
+            pm_error("A raster line continues past width of image");
     }
 }
 
 
 
 static void
-ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP, 
-            pixel ** const colorsP, int ** const dataP, 
-            int * const transparentP) {
+convertRaster(FILE *                const ifP,
+              unsigned int          const cols,
+              unsigned int          const rows, 
+              unsigned int          const charsPerPixel,
+              const ColorNameHash * const colorNameHashP,
+              FILE *                const imageOutFileP,
+              FILE *                const alphaOutFileP) {
 /*----------------------------------------------------------------------------
-   Read the XPM file from stream 'stream'.
+  Read the XPM raster from *ifP and write the PPM raster to *imageOutFileP
+  and the alpha channel to *alphaOutFileP (where those are, respectively,
+  non-null).
 
-   Return the dimensions of the image as *widthP and *heightP.
-   Return the color map as *colorsP, which is an array of *ncolorsP
-   colors.
+  The dimensions are 'cols' by 'rows' and the color map for the XPM
+  raster is *colorNameHashP.
+-----------------------------------------------------------------------------*/
+    char line[MAX_LINE+1];
+    pixel * pixrow;
+    bit * alpharow;
+    unsigned int row;
 
-   Return the raster in newly malloced storage, an array of *widthP by
-   *heightP integers, each of which is an index into the colormap
-   *colorsP (and therefore less than *ncolorsP).  Return the address
-   of the array as *dataP.
+    pixrow   = ppm_allocrow(cols);
+    alpharow = pbm_allocrow(cols);
 
-   In the colormap, put black for the transparent color, if the XPM 
-   image contains one.
------------------------------------------------------------------------------*/
-    char line[MAX_LINE+1], str1[MAX_LINE+1];
-    int totalpixels;
-    int *cursor;  /* cursor into *dataP */
-    int *maxcursor;  /* value of above cursor for last pixel in image */
-    int *ptab;   /* colormap - malloc'ed */
-    int rc;
-    int ncolors;
-    int chars_per_pixel;
+    for (row = 0; row < rows; ++row) {
+        bool haveLine;
 
-    backup = FALSE;
+        for (haveLine = false; !haveLine; ) {
+            getLine(line, sizeof(line), ifP); 
 
-    /* Read the header line */
-    getLine(line, sizeof(line), stream);
-    backup = TRUE;  /* back up so next read reads this line again */
-    
-    rc = sscanf(line, "/* %s */", str1);
-    if (rc == 1 && strncmp(str1, "XPM", 3) == 0) {
-        /* It's an XPM version 3 file */
-        readXpm3Header(stream, widthP, heightP, &chars_per_pixel,
-                       &ncolors, colorsP, &ptab, transparentP);
-    } else {				/* try as an XPM version 1 file */
-        /* Assume it's an XPM version 1 file */
-        readXpm1Header(stream, widthP, heightP, &chars_per_pixel, 
-                       &ncolors, colorsP, &ptab);
-        *transparentP = -1;  /* No transparency in version 1 */
-    }
-    totalpixels = *widthP * *heightP;
-    MALLOCARRAY(*dataP, totalpixels);
-    if (*dataP == NULL)
-        pm_error("Could not get %d bytes of memory for image", totalpixels);
-    cursor = *dataP;
-    maxcursor = *dataP + totalpixels - 1;
-	getLine(line, sizeof(line), stream); 
-        /* read next line (first line may not always start with comment) */
-    while (cursor <= maxcursor) {
-        if (strncmp(line, "/*", 2) == 0) {
-            /* It's a comment.  Ignore it. */
-        } else {
-            interpretXpmLine(line, chars_per_pixel, 
-                             ncolors, ptab, &cursor, maxcursor);
+            if (strneq(line, "/*", 2)) {
+                /* It's a comment.  Ignore it. */
+            } else
+                haveLine = true;
         }
-        if (cursor <= maxcursor)
-            getLine(line, sizeof(line), stream);
+        convertRow(line, cols, charsPerPixel, colorNameHashP,
+                   pixrow, alpharow);
+
+        if (imageOutFileP)
+            ppm_writeppmrow(imageOutFileP, 
+                            pixrow, cols, PPM_MAXMAXVAL, 0);
+            if (alphaOutFileP)
+                pbm_writepbmrow(alphaOutFileP, alpharow, cols, 0);
     }
-    if (ptab) free(ptab);
+
+    pbm_freerow(alpharow);
+    ppm_freerow(pixrow);
 }
  
 
 
 static void
-writeOutput(FILE * const imageout_file,
-            FILE * const alpha_file,
-            int const cols, int const rows, 
-            pixel * const colors, int * const data,
-            int transparent) {
+readXpmHeader(FILE *           const ifP,
+              unsigned int *   const widthP,
+              unsigned int *   const heightP, 
+              unsigned int *   const charsPerPixelP,
+              ColorNameHash ** const colorNameHashPP) {
 /*----------------------------------------------------------------------------
-   Write the image in 'data' to open PPM file stream 'imageout_file',
-   and the alpha mask for it to open PBM file stream 'alpha_file',
-   except if either is NULL, skip it.
+  Read the XPM header, including color map.
 
-   'data' is an array of cols * rows integers, each one being an index
-   into the colormap 'colors'.
-
-   Where the index 'transparent' occurs in 'data', the pixel is supposed
-   to be transparent.  If 'transparent' < 0, no pixels are transparent.
+  In the colormap, put black for the transparent color, if the XPM image
+  contains one.
 -----------------------------------------------------------------------------*/
-    int row;
-    pixel *pixrow;
-    bit * alpharow;
-
-    if (imageout_file)
-        ppm_writeppminit(imageout_file, cols, rows, PPM_MAXMAXVAL, 0);
-    if (alpha_file)
-        pbm_writepbminit(alpha_file, cols, rows, 0);
-
-    pixrow = ppm_allocrow(cols);
-    alpharow = pbm_allocrow(cols);
+    char line[MAX_LINE+1];
+    char str1[MAX_LINE+1];
+    int rc;
+    unsigned int charsPerPixel;
+    unsigned int width, height;
 
-    for (row = 0; row < rows; ++row ) {
-        int col;
-        int * const datarow = data+(row*cols);
+    backup = FALSE;
 
-        for (col = 0; col < cols; ++col) {
-            pixrow[col] = colors[datarow[col]];
-            if (datarow[col] == transparent)
-                alpharow[col] = PBM_BLACK;
-            else
-                alpharow[col] = PBM_WHITE;
-        }
-        if (imageout_file)
-            ppm_writeppmrow(imageout_file, 
-                            pixrow, cols, (pixval) PPM_MAXMAXVAL, 0);
-        if (alpha_file)
-            pbm_writepbmrow(alpha_file, alpharow, cols, 0);
+    /* Read the header line */
+    getLine(line, sizeof(line), ifP);
+    backup = TRUE;  /* back up so next read reads this line again */
+    
+    rc = sscanf(line, "/* %s */", str1);
+    if (rc == 1 && strneq(str1, "XPM", 3)) {
+        /* It's an XPM version 3 file */
+        readXpm3Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP);
+    } else {
+        /* Assume it's an XPM version 1 file */
+        readXpm1Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP);
     }
-    ppm_freerow(pixrow);
-    pbm_freerow(alpharow);
-
-    if (imageout_file)
-        pm_close(imageout_file);
-    if (alpha_file)
-        pm_close(alpha_file);
-}    
-
+    *widthP         = width;
+    *heightP        = height;
+    *charsPerPixelP = charsPerPixel;
+}
+ 
 
 
 int
 main(int argc, char *argv[]) {
 
-    FILE *ifp;
-    FILE *alpha_file, *imageout_file;
-    pixel *colormap;
-    int cols, rows;
-    int transparent;  /* value of 'data' that means transparent */
-    int *data;
-        /* The image as an array of width * height integers, each one
-           being an index int colormap[].
-        */
+    FILE * ifP;
+    FILE * alphaOutFileP;
+    FILE * imageOutFileP;
+    unsigned int cols, rows;
+    unsigned int charsPerPixel;
+    ColorNameHash * colorNameHashP;
 
-    struct cmdline_info cmdline;
+    struct cmdlineInfo cmdline;
 
     ppm_init(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
     verbose = cmdline.verbose;
 
     if ( cmdline.input_filespec != NULL ) 
-        ifp = pm_openr( cmdline.input_filespec);
+        ifP = pm_openr( cmdline.input_filespec);
     else
-        ifp = stdin;
+        ifP = stdin;
 
     if (cmdline.alpha_stdout)
-        alpha_file = stdout;
+        alphaOutFileP = stdout;
     else if (cmdline.alpha_filename == NULL) 
-        alpha_file = NULL;
+        alphaOutFileP = NULL;
     else {
-        alpha_file = pm_openw(cmdline.alpha_filename);
+        alphaOutFileP = pm_openw(cmdline.alpha_filename);
     }
 
     if (cmdline.alpha_stdout) 
-        imageout_file = NULL;
+        imageOutFileP = NULL;
     else
-        imageout_file = stdout;
+        imageOutFileP = stdout;
 
-    ReadXPMFile(ifp, &cols, &rows, &colormap, &data, &transparent);
-    
-    pm_close(ifp);
+    readXpmHeader(ifP, &cols, &rows, &charsPerPixel, &colorNameHashP);
 
-    writeOutput(imageout_file, alpha_file, cols, rows, colormap, data,
-                transparent);
+    if (imageOutFileP)
+        ppm_writeppminit(imageOutFileP, cols, rows, PPM_MAXMAXVAL, 0);
+    if (alphaOutFileP)
+        pbm_writepbminit(alphaOutFileP, cols, rows, 0);
 
-    free(colormap);
+
+    convertRaster(ifP, cols, rows, charsPerPixel, colorNameHashP,
+                  imageOutFileP, alphaOutFileP);
+    
+    pm_close(ifP);
+    if (imageOutFileP)
+        pm_close(imageOutFileP);
+    if (alphaOutFileP)
+        pm_close(alphaOutFileP);
+
+    hash_destroy(colorNameHashP);
     
     return 0;
 }
+
+
+
+/*
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** Upgraded to handle XPM version 3 by
+**   Arnaud Le Hors (lehors@mirsa.inria.fr)
+**   Tue Apr 9 1991
+**
+** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
+**  - Bug fix, no advance of read ptr, would not read 
+**    colors like "ac c black" because it would find 
+**    the "c" of "ac" and then had problems with "c"
+**    as color.
+**    
+**  - Now understands multiword X11 color names
+**  
+**  - Now reads multiple color keys. Takes the color
+**    of the hightest available key. Lines no longer need
+**    to begin with key 'c'.
+**    
+**  - expanded line buffer to from 500 to 2048 for bigger files
+*/
+