about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/colorname.c26
-rw-r--r--lib/colorname.h6
-rw-r--r--lib/libpam.h6
-rw-r--r--lib/libpamcolor.c397
-rw-r--r--lib/libpamn.c140
-rw-r--r--lib/libpbm2.c24
-rw-r--r--lib/libpm.c50
-rw-r--r--lib/libppmcolor.c464
-rw-r--r--lib/libsystem.c1
-rw-r--r--lib/pam.h33
-rw-r--r--lib/util/mallocvar.c8
-rw-r--r--lib/util/mallocvar.h18
-rw-r--r--lib/util/nstring.c75
-rw-r--r--lib/util/nstring.h27
14 files changed, 669 insertions, 606 deletions
diff --git a/lib/colorname.c b/lib/colorname.c
index b2bc4a83..596d8788 100644
--- a/lib/colorname.c
+++ b/lib/colorname.c
@@ -32,7 +32,7 @@ static int lineNo;
 
 
 
-void 
+void
 pm_canonstr(char * const arg) {
 /*----------------------------------------------------------------------------
    Modify string 'arg' to canonical form: lower case, no white space.
@@ -93,7 +93,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
    environment variable whose name is RGB_ENV (e.g. "RGBDEF").  Except
    if that environment variable is not set, it is the first file found,
    if any, in the search path RGB_DB_PATH.
-   
+
    'must_open' is a logical: we must get the file open or die.  If
    'must_open' is true and we can't open the file (e.g. it doesn't
    exist), exit the program with an error message.  If 'must_open' is
@@ -111,7 +111,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
                          "named %s, per the %s environment variable.  "
                          "errno = %d (%s)",
                          rgbdef, RGBENV, errno, strerror(errno));
-        } else {            
+        } else {
             /* The environment variable isn't set, so try the hardcoded
                default color name dictionary locations.
             */
@@ -122,7 +122,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
                          "path '%s' "
                          "and Environment variable %s not set.  Set %s to "
                          "the pathname of your rgb.txt file or don't use "
-                         "color names.", 
+                         "color names.",
                          RGB_DB_PATH, RGBENV, RGBENV);
             }
         }
@@ -131,7 +131,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
         if (f == NULL && must_open)
             pm_error("Can't open the color names dictionary file '%s'.  "
                      "errno = %d (%s)", fileName, errno, strerror(errno));
-        
+
     }
     lineNo = 0;
     return(f);
@@ -154,7 +154,7 @@ pm_colorget(FILE * const f) {
     bool eof;
     struct colorfile_entry retval;
     char * rc;
-    
+
     gotOne = FALSE;  /* initial value */
     eof = FALSE;
     while (!gotOne && !eof) {
@@ -165,15 +165,15 @@ pm_colorget(FILE * const f) {
         else {
             if (buf[0] != '#' && buf[0] != '\n' && buf[0] != '!' &&
                 buf[0] != '\0') {
-                if (sscanf(buf, "%ld %ld %ld %[^\n]", 
-                           &retval.r, &retval.g, &retval.b, colorname) 
+                if (sscanf(buf, "%ld %ld %ld %[^\n]",
+                           &retval.r, &retval.g, &retval.b, colorname)
                     == 4 )
                     gotOne = TRUE;
                 else {
                     if (buf[strlen(buf)-1] == '\n')
                         buf[strlen(buf)-1] = '\0';
                     pm_message("can't parse color names dictionary Line %d:  "
-                               "'%s'", 
+                               "'%s'",
                                lineNo, buf);
                 }
             }
@@ -189,7 +189,7 @@ pm_colorget(FILE * const f) {
 
 
 void
-pm_parse_dictionary_namen(char   const colorname[], 
+pm_parse_dictionary_namen(char   const colorname[],
                           tuplen const color) {
 
     FILE * fP;
@@ -213,7 +213,7 @@ pm_parse_dictionary_namen(char   const colorname[],
             colorfileExhausted = TRUE;
     }
     fclose(fP);
-    
+
     if (!gotit)
         pm_error("unknown color '%s'", colorname);
 
@@ -227,7 +227,7 @@ pm_parse_dictionary_namen(char   const colorname[],
 
 
 void
-pm_parse_dictionary_name(char    const colorname[], 
+pm_parse_dictionary_name(char    const colorname[],
                          pixval  const maxval,
                          int     const closeOk,
                          pixel * const colorP) {
@@ -244,7 +244,7 @@ pm_parse_dictionary_name(char    const colorname[],
     r = ROUNDU(color[PAM_RED_PLANE] * maxval);
     g = ROUNDU(color[PAM_GRN_PLANE] * maxval);
     b = ROUNDU(color[PAM_BLU_PLANE] * maxval);
-    
+
     if (!closeOk) {
         if (maxval != PAM_COLORFILE_MAXVAL) {
             if (fabs((double)r / maxval - color[PAM_RED_PLANE]) > epsilon ||
diff --git a/lib/colorname.h b/lib/colorname.h
index 86c3d585..492df951 100644
--- a/lib/colorname.h
+++ b/lib/colorname.h
@@ -28,7 +28,7 @@ struct colorfile_entry {
 
 
 
-void 
+void
 pm_canonstr(char * const str);
 
 FILE *
@@ -38,11 +38,11 @@ struct colorfile_entry
 pm_colorget(FILE * const f);
 
 void
-pm_parse_dictionary_namen(char   const colorname[], 
+pm_parse_dictionary_namen(char   const colorname[],
                           tuplen const color);
 
 void
-pm_parse_dictionary_name(const char       colorname[], 
+pm_parse_dictionary_name(const char       colorname[],
                          pixval     const maxval,
                          int        const closeOk,
                          pixel *    const colorP);
diff --git a/lib/libpam.h b/lib/libpam.h
index 9f8a34d0..2d4f29d7 100644
--- a/lib/libpam.h
+++ b/lib/libpam.h
@@ -7,9 +7,9 @@
 #include "pgm.h"
 
 void
-pnm_readpaminitrestaspnm(FILE * const fileP, 
-                         int *  const colsP, 
-                         int *  const rowsP, 
+pnm_readpaminitrestaspnm(FILE * const fileP,
+                         int *  const colsP,
+                         int *  const rowsP,
                          gray * const maxvalP,
                          int *  const formatP);
 
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index c0a57dee..49abd063 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -12,7 +12,7 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
+#define _LARGE_FILES
 
 #define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
@@ -20,6 +20,7 @@
 
 #include <string.h>
 #include <limits.h>
+#include <math.h>
 
 #include "netpbm/pm_c_util.h"
 #include "netpbm/mallocvar.h"
@@ -91,7 +92,7 @@ parseHexDigits(const char *   const string,
 
 
 static void
-parseNewHexX11(char   const colorname[], 
+parseNewHexX11(char   const colorname[],
                tuplen const color) {
 /*----------------------------------------------------------------------------
    Determine what color colorname[] specifies in the new style hex
@@ -131,11 +132,11 @@ isNormal(samplen const arg) {
 
 
 static void
-parseNewDecX11(const char * const colorname, 
+parseNewDecX11(const char * const colorname,
                tuplen       const color) {
 
     int rc;
-    
+
     rc = sscanf(colorname, "rgbi:%f/%f/%f",
                 &color[PAM_RED_PLANE],
                 &color[PAM_GRN_PLANE],
@@ -155,7 +156,51 @@ parseNewDecX11(const char * const colorname,
 
 
 static void
-parseOldX11(const char * const colorname, 
+parseInteger(const char * const colorname,
+             tuplen       const color) {
+
+    unsigned int maxval;
+    unsigned int r, g, b;
+    int rc;
+
+    rc = sscanf(colorname, "rgb-%u:%u/%u/%u", &maxval, &r, &g, &b);
+
+    if (rc != 4)
+        pm_error("invalid color specifier '%s'.  "
+                 "If it starts with \"rgb-\", then it must have the format "
+                 "rgb-<MAXVAL>:<RED>:<GRN>:<BLU>, "
+                 "where <MAXVAL>, <RED>, <GRN>, and <BLU> are "
+                 "unsigned integers",
+                 colorname);
+
+    if (maxval < 1 || maxval > PNM_OVERALLMAXVAL)
+        pm_error("Maxval in color specification '%s' is %u, "
+                 "which is invalid because it is not between "
+                 "1 and %u, inclusive",
+                 colorname, maxval, PNM_OVERALLMAXVAL);
+
+    if (r > maxval)
+        pm_error("Red value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, r, maxval);
+    if (g > maxval)
+        pm_error("Green value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, g, maxval);
+    if (b > maxval)
+        pm_error("Blue value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, b, maxval);
+
+    color[PAM_RED_PLANE] = (float)r/maxval;
+    color[PAM_GRN_PLANE] = (float)g/maxval;
+    color[PAM_BLU_PLANE] = (float)b/maxval;
+}
+
+
+
+static void
+parseOldX11(const char * const colorname,
             tuplen       const color) {
 /*----------------------------------------------------------------------------
    Return as *colorP the color specified by the old X11 style color
@@ -181,7 +226,7 @@ parseOldX11(const char * const colorname,
              (samplen)(hexDigitValue(colorname[4]) << 0))
              / 255;
         color[PAM_BLU_PLANE] =
-            ((samplen)(hexDigitValue(colorname[5]) << 4) + 
+            ((samplen)(hexDigitValue(colorname[5]) << 4) +
              (samplen)(hexDigitValue(colorname[6]) << 0))
              / 255;
         break;
@@ -193,12 +238,12 @@ parseOldX11(const char * const colorname,
              (samplen)(hexDigitValue(colorname[3]) << 0))
             / 4095;
         color[PAM_GRN_PLANE] =
-            ((samplen)(hexDigitValue(colorname[4]) << 8) + 
+            ((samplen)(hexDigitValue(colorname[4]) << 8) +
              (samplen)(hexDigitValue(colorname[5]) << 4) +
              (samplen)(hexDigitValue(colorname[6]) << 0))
             / 4095;
         color[PAM_BLU_PLANE] =
-            ((samplen)(hexDigitValue(colorname[7]) << 8) + 
+            ((samplen)(hexDigitValue(colorname[7]) << 8) +
              (samplen)(hexDigitValue(colorname[8]) << 4) +
              (samplen)(hexDigitValue(colorname[9]) << 0))
             / 4095;
@@ -206,19 +251,19 @@ parseOldX11(const char * const colorname,
 
     case 12:
         color[PAM_RED_PLANE] =
-            ((samplen)(hexDigitValue(colorname[1]) << 12) + 
+            ((samplen)(hexDigitValue(colorname[1]) << 12) +
              (samplen)(hexDigitValue(colorname[2]) <<  8) +
-             (samplen)(hexDigitValue(colorname[3]) <<  4) + 
+             (samplen)(hexDigitValue(colorname[3]) <<  4) +
              (samplen)(hexDigitValue(colorname[4]) <<  0))
             / 65535;
         color[PAM_GRN_PLANE] =
-            ((samplen)(hexDigitValue(colorname[5]) << 12) + 
+            ((samplen)(hexDigitValue(colorname[5]) << 12) +
              (samplen)(hexDigitValue(colorname[6]) <<  8) +
              (samplen)(hexDigitValue(colorname[7]) <<  4) +
              (samplen)(hexDigitValue(colorname[8]) <<  0))
             / 65535;
         color[PAM_BLU_PLANE] =
-            ((samplen)(hexDigitValue(colorname[ 9]) << 12) + 
+            ((samplen)(hexDigitValue(colorname[ 9]) << 12) +
              (samplen)(hexDigitValue(colorname[10]) << 8) +
              (samplen)(hexDigitValue(colorname[11]) << 4) +
              (samplen)(hexDigitValue(colorname[12]) << 0))
@@ -233,7 +278,7 @@ parseOldX11(const char * const colorname,
 
 
 static void
-parseOldX11Dec(const char* const colorname, 
+parseOldX11Dec(const char* const colorname,
                tuplen      const color) {
 
     int rc;
@@ -260,15 +305,18 @@ tuplen
 pnm_parsecolorn(const char * const colorname) {
 
     tuplen retval;
-    
+
     MALLOCARRAY_NOFAIL(retval, 3);
 
-    if (strncmp(colorname, "rgb:", 4) == 0)
+    if (strneq(colorname, "rgb:", 4))
         /* It's a new-X11-style hexadecimal rgb specifier. */
         parseNewHexX11(colorname, retval);
-    else if (strncmp(colorname, "rgbi:", 5) == 0)
+    else if (strneq(colorname, "rgbi:", 5))
         /* It's a new-X11-style decimal/float rgb specifier. */
         parseNewDecX11(colorname, retval);
+    else if (strneq(colorname, "rgb-", 4))
+        /* It's a Netpbm-native decimal integer rgb specifier */
+        parseInteger(colorname, retval);
     else if (colorname[0] == '#')
         /* It's an old-X11-style hexadecimal rgb specifier. */
         parseOldX11(colorname, retval);
@@ -276,7 +324,7 @@ pnm_parsecolorn(const char * const colorname) {
              colorname[0] == '.')
         /* It's an old-style decimal/float rgb specifier. */
         parseOldX11Dec(colorname, retval);
-    else 
+    else
         /* Must be a name from the X-style rgb file. */
         pm_parse_dictionary_namen(colorname, retval);
 
@@ -285,9 +333,29 @@ pnm_parsecolorn(const char * const colorname) {
 
 
 
+static void
+warnIfNotExact(const char * const colorname,
+               tuple        const rounded,
+               tuplen       const exact,
+               sample       const maxval,
+               unsigned int const plane) {
+
+    float const epsilon = 1.0/65536.0;
+
+    if (fabs((float)(rounded[plane] / maxval) - exact[plane]) > epsilon) {
+        pm_message("WARNING: Component %u of color '%s' is %f, "
+                   "which cannot be represented precisely with maxval %lu.  "
+                   "Approximating as %lu.",
+                   plane, colorname, exact[plane], maxval, rounded[plane]);
+    }
+}
+
+
+
 tuple
-pnm_parsecolor(const char * const colorname,
-               sample       const maxval) {
+pnm_parsecolor2(const char * const colorname,
+                sample       const maxval,
+                int          const closeOk) {
 
     tuple retval;
     tuplen color;
@@ -306,6 +374,12 @@ pnm_parsecolor(const char * const colorname,
     retval[PAM_GRN_PLANE] = ROUNDU(color[PAM_GRN_PLANE] * maxval);
     retval[PAM_BLU_PLANE] = ROUNDU(color[PAM_BLU_PLANE] * maxval);
 
+    if (!closeOk) {
+        warnIfNotExact(colorname, retval, color, maxval, PAM_RED_PLANE);
+        warnIfNotExact(colorname, retval, color, maxval, PAM_GRN_PLANE);
+        warnIfNotExact(colorname, retval, color, maxval, PAM_BLU_PLANE);
+    }
+
     free(color);
 
     return retval;
@@ -313,6 +387,15 @@ pnm_parsecolor(const char * const colorname,
 
 
 
+tuple
+pnm_parsecolor(const char * const colorname,
+               sample       const maxval) {
+
+    return pnm_parsecolor2(colorname, maxval, true);
+}
+
+
+
 const char *
 pnm_colorname(struct pam * const pamP,
               tuple        const color,
@@ -324,7 +407,7 @@ pnm_colorname(struct pam * const pamP,
 
     if (pamP->depth < 3)
         PPM_ASSIGN(colorp, color[0], color[0], color[0]);
-    else 
+    else
         PPM_ASSIGN(colorp,
                    color[PAM_RED_PLANE],
                    color[PAM_GRN_PLANE],
@@ -341,21 +424,279 @@ pnm_colorname(struct pam * const pamP,
 
 
 
+static tuple
+scaledRgb(struct pam * const pamP,
+          tuple        const color,
+          sample       const maxval) {
+
+    tuple scaledColor;
+
+    struct pam pam;
+
+    pam.size             = sizeof(pam);
+    pam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    pam.maxval           = pamP->maxval;
+    pam.depth            = pamP->depth;
+    pam.allocation_depth = 3;
+
+    scaledColor = pnm_allocpamtuple(&pam);
+
+    pnm_scaletuple(&pam, scaledColor, color, maxval);
+
+    pnm_maketuplergb(&pam, scaledColor);
+
+    return scaledColor;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_integer(struct pam * const pamP,
+                          tuple        const color,
+                          sample       const maxval) {
+
+    const char * retval;
+
+    tuple scaledColor = scaledRgb(pamP, color, maxval);
+
+    pm_asprintf(&retval, "rgb-%lu:%lu/%lu/%lu",
+                maxval,
+                scaledColor[PAM_RED_PLANE],
+                scaledColor[PAM_GRN_PLANE],
+                scaledColor[PAM_BLU_PLANE]
+        );
+
+    pnm_freepamtuple(scaledColor);
+
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_norm(struct pam * const pamP,
+                       tuple        const color,
+                       unsigned int const digitCt) {
+
+    const char * retval;
+
+    tuple rgbColor;
+
+    tuplen normColor;
+
+    struct pam rgbPam;
+
+    rgbPam.size             = sizeof(rgbPam);
+    rgbPam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    rgbPam.maxval           = pamP->maxval;
+    rgbPam.depth            = pamP->depth;
+    rgbPam.allocation_depth = 3;
+
+    rgbColor = pnm_allocpamtuple(&rgbPam);
+
+    pnm_assigntuple(&rgbPam, rgbColor, color);  /* initial value */
+
+    pnm_maketuplergb(&rgbPam, rgbColor);
+
+    normColor = pnm_allocpamtuplen(&rgbPam);
+
+    rgbPam.depth = 3;
+
+    pnm_normalizetuple(&rgbPam, rgbColor, normColor);
+
+    {
+        const char * format;
+        pm_asprintf(&format, "rgbi:%%.%uf/%%.%uf/%%.%uf",
+                    digitCt, digitCt, digitCt);
+
+        pm_asprintf(&retval, format,
+                    normColor[PAM_RED_PLANE],
+                    normColor[PAM_GRN_PLANE],
+                    normColor[PAM_BLU_PLANE]
+            );
+        pm_strfree(format);
+    }
+
+    pnm_freepamtuplen(normColor);
+    pnm_freepamtuple(rgbColor);
+
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_x11(struct pam * const pamP,
+                      tuple        const color,
+                      unsigned int const hexDigitCt) {
+
+    const char * retval;
+
+    sample maxval;
+    const char * format;
+
+    switch(hexDigitCt) {
+    case 1:
+        maxval =    15;
+        format = "rgb:%01x:%01x:%01x";
+        break;
+    case 2:
+        maxval =   255;
+        format = "rgb:%02x:%02x:%02x";
+        break;
+    case 3:
+        maxval =  4095;
+        format = "rgb:%03x:%03x:%03x";
+        break;
+    case 4:
+        maxval = 65535;
+        format = "rgb:%04x:%04x:%04x";
+        break;
+    default:
+        pm_error("Invalid number of hex digits "
+                 "for X11 color specification: %u.  "
+                 "Must be 1, 2, 3, or 4", hexDigitCt);
+    }
+
+    {
+        tuple const scaledColor = scaledRgb(pamP, color, maxval);
+
+        pm_asprintf(&retval, format,
+                    scaledColor[PAM_RED_PLANE],
+                    scaledColor[PAM_GRN_PLANE],
+                    scaledColor[PAM_BLU_PLANE]
+            );
+
+        pnm_freepamtuple(scaledColor);
+    }
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_dict(struct pam * const pamP,
+                   tuple        const color) {
+/*----------------------------------------------------------------------------
+   Return the name from the color dictionary of color 'color'.
+
+   Return it in newly allocated pm_strdrup storage.
+
+   If the color is not in the dictionary, or the dictionary doesn't even
+   exist (file not found in any of the possible places), return NULL.
+
+   The color dictionary uses maxval 255, so we match to that precision.
+   E.g. if a component of 'color' is 1000 out of maxval 65535 (which would be
+   3.9 out of maxval 255), we consider it a match to a component value of 4
+   in the color dictionary.
+-----------------------------------------------------------------------------*/
+    tuple scaledColor = scaledRgb(pamP, color, PAM_COLORFILE_MAXVAL);
+
+    FILE * dictFileP;
+    const char * colorname;
+
+    dictFileP = pm_openColornameFile(NULL, false);
+
+    if (dictFileP) {
+        bool eof;
+        for (colorname = NULL, eof = false; !colorname && !eof; ) {
+            struct colorfile_entry const ce = pm_colorget(dictFileP);
+
+            if (ce.colorname)  {
+                if (scaledColor[PAM_RED_PLANE] == (sample)ce.r &&
+                    scaledColor[PAM_GRN_PLANE] == (sample)ce.g &&
+                    scaledColor[PAM_BLU_PLANE] == (sample)ce.b) {
+                    colorname = pm_strdup(ce.colorname);
+                }
+            } else
+                eof = TRUE;
+        }
+
+        fclose(dictFileP);
+    } else
+        colorname = NULL;
+
+    pnm_freepamtuple(scaledColor);
+
+    return colorname;
+}
+
+
+
+const char *
+pnm_colorspec_dict_close(struct pam * const pamP,
+                         tuple        const color) {
+/*----------------------------------------------------------------------------
+   Return the name from the color dictionary of the color closst to 'color'.
+
+   Return it in newly allocated pm_strdrup storage.
+
+   If the color dictionary is empty, or the dictionary doesn't even exist
+   (file not found in any of the possible places), return a null string.
+   This is the only case in which we would return a null string, as the
+   color dictionary cannot define a null string color name.
+-----------------------------------------------------------------------------*/
+    tuple scaledColor = scaledRgb(pamP, color, PAM_COLORFILE_MAXVAL);
+
+    FILE * dictFileP;
+    static char colorname[200];
+
+    dictFileP = pm_openColornameFile(NULL, false);
+
+    if (dictFileP) {
+        unsigned int bestDiff;
+        bool eof;
+
+        for (bestDiff = 32767, eof = FALSE; !eof && bestDiff > 0; ) {
+            struct colorfile_entry const ce = pm_colorget(dictFileP);
+
+            if (ce.colorname)  {
+                unsigned int const thisDiff =
+                    abs((int)scaledColor[PAM_RED_PLANE] - (int)ce.r) +
+                    abs((int)scaledColor[PAM_GRN_PLANE] - (int)ce.g) +
+                    abs((int)scaledColor[PAM_BLU_PLANE] - (int)ce.b);
+
+                if (thisDiff < bestDiff) {
+                    bestDiff = thisDiff;
+                    STRSCPY(colorname, ce.colorname);
+                }
+            } else
+                eof = TRUE;
+        }
+
+        fclose(dictFileP);
+
+        if (bestDiff == 32767) {
+            /* Color file contain no entries, so we can't even pick a close
+               one
+            */
+            STRSCPY(colorname, "");
+        }
+    } else
+        STRSCPY(colorname, "");
+
+    pnm_freepamtuple(scaledColor);
+
+    return pm_strdup(colorname);
+}
+
+
+
 double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB};
 
 void
-pnm_YCbCrtuple(tuple    const tuple, 
-               double * const YP, 
-               double * const CbP, 
+pnm_YCbCrtuple(tuple    const tuple,
+               double * const YP,
+               double * const CbP,
                double * const CrP) {
 /*----------------------------------------------------------------------------
-   Assuming that the tuple 'tuple' is of tupletype RGB, return the 
+   Assuming that the tuple 'tuple' is of tupletype RGB, return the
    Y/Cb/Cr representation of the color represented by the tuple.
 -----------------------------------------------------------------------------*/
     int const red = (int)tuple[PAM_RED_PLANE];
     int const grn = (int)tuple[PAM_GRN_PLANE];
     int const blu = (int)tuple[PAM_BLU_PLANE];
-    
+
     *YP  = (+ PPM_LUMINR * red + PPM_LUMING * grn + PPM_LUMINB * blu);
     *CbP = (- 0.16874 * red - 0.33126 * grn + 0.50000 * blu);
     *CrP = (+ 0.50000 * red - 0.41869 * grn - 0.08131 * blu);
@@ -363,11 +704,11 @@ pnm_YCbCrtuple(tuple    const tuple,
 
 
 
-void 
+void
 pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
                       tuple              const tuple,
                       double             const Y,
-                      double             const Cb, 
+                      double             const Cb,
                       double             const Cr,
                       int *              const overflowP) {
 
@@ -388,7 +729,7 @@ pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
         } else if (rgb[plane] < 0.0) {
             overflow = TRUE;
             tuple[plane] = 0;
-        } else 
+        } else
             tuple[plane] = (sample)rgb[plane];
     }
     if (overflowP)
diff --git a/lib/libpamn.c b/lib/libpamn.c
index 26dbbfe3..65c8979f 100644
--- a/lib/libpamn.c
+++ b/lib/libpamn.c
@@ -23,6 +23,43 @@
 
 
 
+static unsigned int
+allocationDepth(const struct pam * const pamP) {
+
+    unsigned int retval;
+
+    if (pamP->len >= PAM_STRUCT_SIZE(allocation_depth)) {
+        if (pamP->allocation_depth == 0)
+            retval = pamP->depth;
+        else {
+            if (pamP->depth > pamP->allocation_depth)
+                pm_error("'allocationDepth' (%u) is smaller than 'depth' (%u)",
+                         pamP->allocation_depth, pamP->depth);
+            retval = pamP->allocation_depth;
+        }
+    } else
+        retval = pamP->depth;
+    return retval;
+}
+
+
+
+tuplen
+pnm_allocpamtuplen(const struct pam * const pamP) {
+
+    tuplen retval;
+
+    retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
+
+    if (retval == NULL)
+        pm_error("Out of memory allocating %u-plane normalized tuple",
+                 allocationDepth(pamP));
+
+    return retval;
+}
+
+
+
 static void
 allocpamrown(const struct pam * const pamP,
              tuplen **          const tuplerownP,
@@ -32,7 +69,7 @@ allocpamrown(const struct pam * const pamP,
    overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
    ensures this assumption is valid.
 -----------------------------------------------------------------------------*/
-    int const bytes_per_tuple = pamP->depth * sizeof(samplen);
+    int const bytes_per_tuple = allocationDepth(pamP) * sizeof(samplen);
 
     tuplen * tuplerown;
     const char * error;
@@ -47,15 +84,16 @@ allocpamrown(const struct pam * const pamP,
         pm_asprintf(&error, "Out of memory allocating space for a tuple row of"
                     "%u tuples by %u samples per tuple "
                     "by %u bytes per sample.",
-                    pamP->width, pamP->depth, (unsigned)sizeof(samplen));
+                    pamP->width, allocationDepth(pamP),
+                    (unsigned)sizeof(samplen));
     else {
         /* Now we initialize the pointers to the individual tuples to make this
            a regulation C two dimensional array.
         */
-        
+
         unsigned char * p;
         unsigned int i;
-        
+
         p = (unsigned char*) (tuplerown + pamP->width);
             /* location of Tuple 0 */
         for (i = 0; i < pamP->width; ++i) {
@@ -93,7 +131,7 @@ pnm_allocpamrown(const struct pam * const pamP) {
 
 
 static void
-readpbmrow(const struct pam * const pamP, 
+readpbmrow(const struct pam * const pamP,
            tuplen *           const tuplenrow) {
 
     bit * bitrow;
@@ -101,7 +139,7 @@ readpbmrow(const struct pam * const pamP,
     jmp_buf * origJmpbufP;
 
     bitrow = pbm_allocrow(pamP->width);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pbm_freerow(bitrow);
         pm_setjmpbuf(origJmpbufP);
@@ -123,15 +161,15 @@ readpbmrow(const struct pam * const pamP,
 
 
 static void
-readpamrow(const struct pam * const pamP, 
+readpamrow(const struct pam * const pamP,
            tuplen *           const tuplenrow) {
 
     jmp_buf jmpbuf;
     jmp_buf * origJmpbufP;
     tuple * tuplerow;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamrow(tuplerow);
         pm_setjmpbuf(origJmpbufP);
@@ -159,13 +197,13 @@ readpamrow(const struct pam * const pamP,
 
 
 
-void 
-pnm_readpamrown(const struct pam * const pamP, 
+void
+pnm_readpamrown(const struct pam * const pamP,
                 tuplen *           const tuplenrow) {
 
-    /* For speed, we don't check any of the inputs for consistency 
+    /* For speed, we don't check any of the inputs for consistency
        here (unless it's necessary to avoid crashing).  Any consistency
-       checking should have been done by a prior call to 
+       checking should have been done by a prior call to
        pnm_writepaminit().
     */
     assert(pamP->maxval != 0);
@@ -186,7 +224,7 @@ pnm_readpamrown(const struct pam * const pamP,
 
 
 static void
-writepbmrow(const struct pam * const pamP, 
+writepbmrow(const struct pam * const pamP,
             const tuplen *     const tuplenrow) {
 
     jmp_buf jmpbuf;
@@ -206,26 +244,26 @@ writepbmrow(const struct pam * const pamP,
 
         for (col = 0; col < pamP->width; ++col)
             bitrow[col] = tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE;
-        pbm_writepbmrow(pamP->file, bitrow, pamP->width, 
+        pbm_writepbmrow(pamP->file, bitrow, pamP->width,
                         pamP->format == PBM_FORMAT);
 
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
-} 
+}
 
 
 
 static void
-writepamrow(const struct pam * const pamP, 
+writepamrow(const struct pam * const pamP,
             const tuplen *     const tuplenrow) {
 
     jmp_buf jmpbuf;
     jmp_buf * origJmpbufP;
     tuple * tuplerow;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamrow(tuplerow);
         pm_setjmpbuf(origJmpbufP);
@@ -240,7 +278,7 @@ writepamrow(const struct pam * const pamP,
             for (plane = 0; plane < pamP->depth; ++plane)
                 tuplerow[col][plane] = (sample)
                     (tuplenrow[col][plane] * pamP->maxval + 0.5);
-        }    
+        }
         pnm_writepamrow(pamP, tuplerow);
 
         pm_setjmpbuf(origJmpbufP);
@@ -250,13 +288,13 @@ writepamrow(const struct pam * const pamP,
 
 
 
-void 
-pnm_writepamrown(const struct pam * const pamP, 
+void
+pnm_writepamrown(const struct pam * const pamP,
                  const tuplen *     const tuplenrow) {
 
-    /* For speed, we don't check any of the inputs for consistency 
+    /* For speed, we don't check any of the inputs for consistency
        here (unless it's necessary to avoid crashing).  Any consistency
-       checking should have been done by a prior call to 
+       checking should have been done by a prior call to
        pnm_writepaminit().
     */
     assert(pamP->maxval != 0);
@@ -274,16 +312,16 @@ pnm_writepamrown(const struct pam * const pamP,
 
 tuplen **
 pnm_allocpamarrayn(const struct pam * const pamP) {
-    
+
     tuplen ** tuplenarray;
     const char * error;
 
     /* If the speed of this is ever an issue, it might be sped up a little
        by allocating one large chunk.
     */
-    
+
     MALLOCARRAY(tuplenarray, pamP->height);
-    if (tuplenarray == NULL) 
+    if (tuplenarray == NULL)
         pm_asprintf(&error,
                     "Out of memory allocating the row pointer section of "
                     "a %u row array", pamP->height);
@@ -317,7 +355,7 @@ pnm_allocpamarrayn(const struct pam * const pamP) {
 
 
 void
-pnm_freepamarrayn(tuplen **          const tuplenarray, 
+pnm_freepamarrayn(tuplen **          const tuplenarray,
                   const struct pam * const pamP) {
 
     int row;
@@ -329,9 +367,9 @@ pnm_freepamarrayn(tuplen **          const tuplenarray,
 
 
 
-tuplen** 
-pnm_readpamn(FILE *       const file, 
-             struct pam * const pamP, 
+tuplen**
+pnm_readpamn(FILE *       const file,
+             struct pam * const pamP,
              int          const size) {
 
     tuplen **tuplenarray;
@@ -339,9 +377,9 @@ pnm_readpamn(FILE *       const file,
     jmp_buf * origJmpbufP;
 
     pnm_readpaminit(file, pamP, size);
-    
+
     tuplenarray = pnm_allocpamarrayn(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamarrayn(tuplenarray, pamP);
         pm_setjmpbuf(origJmpbufP);
@@ -351,7 +389,7 @@ pnm_readpamn(FILE *       const file,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        for (row = 0; row < pamP->height; ++row) 
+        for (row = 0; row < pamP->height; ++row)
             pnm_readpamrown(pamP, tuplenarray[row]);
 
         pm_setjmpbuf(origJmpbufP);
@@ -361,15 +399,15 @@ pnm_readpamn(FILE *       const file,
 
 
 
-void 
-pnm_writepamn(struct pam * const pamP, 
+void
+pnm_writepamn(struct pam * const pamP,
               tuplen **    const tuplenarray) {
 
     unsigned int row;
 
     pnm_writepaminit(pamP);
-    
-    for (row = 0; row < pamP->height; ++row) 
+
+    for (row = 0; row < pamP->height; ++row)
         pnm_writepamrown(pamP, tuplenarray[row]);
 }
 
@@ -382,7 +420,7 @@ pnm_normalizetuple(struct pam * const pamP,
 
     unsigned int plane;
 
-    for (plane = 0; plane < pamP->depth; ++plane) 
+    for (plane = 0; plane < pamP->depth; ++plane)
         tuplen[plane] = (samplen)tuple[plane] / pamP->maxval;
 }
 
@@ -395,7 +433,7 @@ pnm_unnormalizetuple(struct pam * const pamP,
 
     unsigned int plane;
 
-    for (plane = 0; plane < pamP->depth; ++plane) 
+    for (plane = 0; plane < pamP->depth; ++plane)
         tuple[plane] = tuplen[plane] * pamP->maxval + 0.5;
 }
 
@@ -412,7 +450,7 @@ pnm_normalizeRow(struct pam *             const pamP,
            once here so we can multiply many times later.
         */
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (transform && transform[plane]) {
             unsigned int col;
@@ -437,14 +475,14 @@ reversemap(samplen          const samplen,
 /*----------------------------------------------------------------------------
    Find the integer sample value that maps to the normalized samplen value
    'samplen' through the map 'transformMap'.  We interpret the map as
-   mapping the value N+1 to all the values transformMap[N] through 
+   mapping the value N+1 to all the values transformMap[N] through
    transformMap[N+1], and we expect transformMap[N+1] to be greater than
    transformMap[N] for all N.
 -----------------------------------------------------------------------------*/
     /* Do a binary search, since the values are in sorted (increasing)
        order
     */
-    
+
     sample low, high;
 
     low = 0; high = maxval;  /* Consider whole range to start */
@@ -471,18 +509,18 @@ pnm_unnormalizeRow(struct pam *             const pamP,
                    tuple *                  const tuplerow) {
 
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (transform && transform[plane]) {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col)
-                tuplerow[col][plane] = 
-                    reversemap(tuplenrow[col][plane], 
+                tuplerow[col][plane] =
+                    reversemap(tuplenrow[col][plane],
                                transform[plane], pamP->maxval);
         } else {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col)
-                tuplerow[col][plane] = 
+                tuplerow[col][plane] =
                     tuplenrow[col][plane] * pamP->maxval + 0.5;
         }
     }
@@ -500,13 +538,13 @@ gammaCommon(struct pam *  const pamP,
     unsigned int plane;
     unsigned int opacityPlane;
     int haveOpacity;
-    
+
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (haveOpacity && plane == opacityPlane) {
             /* It's an opacity (alpha) plane, which means there is
-               no gamma adjustment in it.  
+               no gamma adjustment in it.
             */
         } else {
             unsigned int col;
@@ -550,7 +588,7 @@ applyopacityCommon(enum applyUnapply const applyUnapply,
 -----------------------------------------------------------------------------*/
     unsigned int opacityPlane;
     int haveOpacity;
-    
+
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
     if (haveOpacity) {
@@ -657,7 +695,7 @@ createUngammaMapOffset(const struct pam * const pamP,
                     retval[plane] = NULL;
                 else
                     retval[plane] = ungammaTransformMap;
-            }            
+            }
             fillInMap(ungammaTransformMap, pamP->maxval, offset);
         } else {
             free(retval);
diff --git a/lib/libpbm2.c b/lib/libpbm2.c
index f199c51a..a35004f9 100644
--- a/lib/libpbm2.c
+++ b/lib/libpbm2.c
@@ -18,7 +18,7 @@
 #include "fileio.h"
 #include "pam.h"
 
-static bit 
+static bit
 getbit (FILE * const file) {
     char ch;
 
@@ -28,7 +28,7 @@ getbit (FILE * const file) {
 
     if ( ch != '0' && ch != '1' )
         pm_error( "junk in file where bits should be" );
-    
+
     return ( ch == '1' ) ? 1 : 0;
 }
 
@@ -157,9 +157,9 @@ pbm_readpbmrow( FILE * const file,
 
 
 void
-pbm_readpbmrow_packed(FILE *          const fileP, 
+pbm_readpbmrow_packed(FILE *          const fileP,
                       unsigned char * const packedBits,
-                      int             const cols, 
+                      int             const cols,
                       int             const format) {
 
     switch(format) {
@@ -182,20 +182,20 @@ pbm_readpbmrow_packed(FILE *          const fileP,
     case RPBM_FORMAT: {
         unsigned int bytesReadCt;
         bytesReadCt = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
-             
+
         if (bytesReadCt < pbm_packed_bytes(cols)) {
-            if (feof(fileP)) 
-                if (bytesReadCt == 0) 
+            if (feof(fileP))
+                if (bytesReadCt == 0)
                     pm_error("Attempt to read a raw PBM image row, but "
                              "no more rows left in file.");
                 else
                     pm_error("EOF in the middle of a raw PBM row.");
-            else 
+            else
                 pm_error("I/O error reading raw PBM row");
         }
     }
     break;
-    
+
     default:
         pm_error("Internal error in pbm_readpbmrow_packed.");
     }
@@ -205,7 +205,7 @@ pbm_readpbmrow_packed(FILE *          const fileP,
 
 void
 pbm_readpbmrow_bitoffset(FILE *          const ifP,
-                         unsigned char * const packedBits, 
+                         unsigned char * const packedBits,
                          int             const cols,
                          int             const format,
                          unsigned int    const offset) {
@@ -237,7 +237,7 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
         /* Target slot doesn't start on byte boundary; right-shift. */
         unsigned char carryover;
         unsigned int i;
-  
+
         carryover = (origHead >> lsh) << lsh;
 
         for (i = 0; i <= last; ++i) {
@@ -246,7 +246,7 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
             carryover = t;
         }
     }
-  
+
     if ((cols + rsh) % 8 > 0) {
         /* Adjust rightmost char */
         unsigned int  const trs = (cols + rsh) % 8;
diff --git a/lib/libpm.c b/lib/libpm.c
index f9aa1aef..b335debb 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -45,7 +45,7 @@ const char * pm_progname;
 int pm_plain_output;
     /* Boolean: programs should produce output in plain format */
 
-static bool pm_showmessages;  
+static bool pm_showmessages;
     /* Programs should display informational messages (because the user didn't
        specify the --quiet option).
     */
@@ -249,7 +249,7 @@ pm_errormsg(const char format[], ...) {
     va_start(args, format);
 
     pm_vasprintf(&msg, format, args);
-    
+
     errormsg(msg);
 
     pm_strfree(msg);
@@ -267,7 +267,7 @@ pm_error(const char format[], ...) {
     va_start(args, format);
 
     pm_vasprintf(&msg, format, args);
-    
+
     errormsg(msg);
 
     pm_strfree(msg);
@@ -356,7 +356,7 @@ pm_allocarray(int const cols,
 
 
 void
-pm_freearray(char ** const rowIndex, 
+pm_freearray(char ** const rowIndex,
              int     const rows) {
 
     void * const rowIndexVoid = rowIndex;
@@ -369,8 +369,8 @@ pm_freearray(char ** const rowIndex,
 /* Case-insensitive keyword matcher. */
 
 int
-pm_keymatch(const char *       const strarg, 
-            const char * const keywordarg, 
+pm_keymatch(const char *       const strarg,
+            const char * const keywordarg,
             int          const minchars) {
     int len;
     const char * keyword;
@@ -449,7 +449,7 @@ pm_bitstomaxval(int const bits) {
 
 
 unsigned int PURE_FN_ATTR
-pm_lcm(unsigned int const x, 
+pm_lcm(unsigned int const x,
        unsigned int const y,
        unsigned int const z,
        unsigned int const limit) {
@@ -472,7 +472,7 @@ pm_lcm(unsigned int const x,
            candidate <= limit)
         candidate += biggest;
 
-    if (candidate > limit) 
+    if (candidate > limit)
         candidate = limit;
 
     return candidate;
@@ -499,7 +499,7 @@ pm_init(const char * const progname,
 #ifdef HAVE_SETMODE
     /* Set the stdin and stdout mode to binary.  This means nothing on Unix,
        but matters on Windows.
-       
+
        Note that stdin and stdout aren't necessarily image files.  In
        particular, stdout is sometimes text for human consumption,
        typically printed on the terminal.  Binary mode isn't really
@@ -507,7 +507,7 @@ pm_init(const char * const progname,
        any knowledge of how stdin and stdout are being used because it is
        easy.  But we do make an exception for the case that we know the
        file is a terminal, to get a little closer to doing the right
-       thing.  
+       thing.
     */
     if (!isatty(0)) setmode(0,O_BINARY);  /* Standard Input */
     if (!isatty(1)) setmode(1,O_BINARY);  /* Standard Output */
@@ -525,7 +525,7 @@ dtMsg(time_t const dateTime) {
     struct tm * const brokenTimeP = localtime(&dateTime);
 
     char buffer[100];
-    
+
     strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
 
     return pm_strdup(buffer);
@@ -579,7 +579,7 @@ showVersion(void) {
         pm_message( "RGB_ENV='%s'", RGBENV );
         rgbdef = getenv(RGBENV);
         if( rgbdef )
-            pm_message( "RGBENV= '%s' (env vbl set to '%s')", 
+            pm_message( "RGBENV= '%s' (env vbl set to '%s')",
                         RGBENV, rgbdef );
         else
             pm_message( "RGBENV= '%s' (env vbl is unset)", RGBENV);
@@ -592,7 +592,7 @@ static void
 showNetpbmHelp(const char progname[]) {
 /*----------------------------------------------------------------------------
   Tell the user where to get help for this program, assuming it is a Netpbm
-  program (a program that comes with the Netpbm package, as opposed to a 
+  program (a program that comes with the Netpbm package, as opposed to a
   program that just uses the Netpbm libraries).
 
   Tell him to go to the URL listed in the Netpbm configuration file.
@@ -610,9 +610,9 @@ showNetpbmHelp(const char progname[]) {
 
     if (getenv("NETPBM_CONF"))
         netpbmConfigFileName = getenv("NETPBM_CONF");
-    else 
+    else
         netpbmConfigFileName = "/etc/netpbm";
-    
+
     netpbmConfigFile = fopen(netpbmConfigFileName, "r");
     if (netpbmConfigFile == NULL) {
         pm_message("Unable to open Netpbm configuration file '%s'.  "
@@ -666,14 +666,14 @@ parseCommonOptions(int *         const argcP,
 
     for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
             if (strcaseeq(argv[inCursor], "-quiet") ||
-                strcaseeq(argv[inCursor], "--quiet")) 
+                strcaseeq(argv[inCursor], "--quiet"))
                 *showMessagesP = false;
             else if (strcaseeq(argv[inCursor], "-version") ||
-                     strcaseeq(argv[inCursor], "--version")) 
+                     strcaseeq(argv[inCursor], "--version"))
                 *showVersionP = true;
             else if (strcaseeq(argv[inCursor], "-help") ||
                      strcaseeq(argv[inCursor], "--help") ||
-                     strcaseeq(argv[inCursor], "-?")) 
+                     strcaseeq(argv[inCursor], "-?"))
                 *showHelpP = true;
             else if (strcaseeq(argv[inCursor], "-plain") ||
                      strcaseeq(argv[inCursor], "--plain"))
@@ -728,7 +728,7 @@ pm_proginit(int *         const argcP,
         exit(0);
     } else if (justShowHelp) {
         pm_error("Use 'man %s' for help.", progname);
-        /* If we can figure out a way to distinguish Netpbm programs from 
+        /* If we can figure out a way to distinguish Netpbm programs from
            other programs using the Netpbm libraries, we can do better here.
         */
         if (0)
@@ -742,7 +742,7 @@ pm_proginit(int *         const argcP,
 void
 pm_setMessage(int   const newState,
               int * const oldStateP) {
-    
+
     if (oldStateP)
         *oldStateP = pm_showmessages;
 
@@ -763,7 +763,7 @@ static void
 extractAfterLastSlash(const char * const fullPath,
                       char *       const retval,
                       size_t       const retvalSize) {
-    
+
     char * slashPos;
 
     /* Chop any directories off the left end */
@@ -794,13 +794,13 @@ chopOffExe(char * const arg) {
 char *
 pm_arg0toprogname(const char arg0[]) {
 /*----------------------------------------------------------------------------
-   Given a value for argv[0] (a command name or file name passed to a 
+   Given a value for argv[0] (a command name or file name passed to a
    program in the standard C calling sequence), return the name of the
    Netpbm program to which it refers.
 
    In the most ordinary case, this is simply the argument itself.
 
-   But if the argument contains a slash, it is the part of the argument 
+   But if the argument contains a slash, it is the part of the argument
    after the last slash, and if there is a .exe on it (as there is for
    DJGPP), that is removed.
 
@@ -848,7 +848,7 @@ pm_parse_width(const char * const arg) {
     unsigned int width;
     const char * error;
 
-    pm_interpret_uint(arg, &width, &error);
+    pm_string_to_uint(arg, &width, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image width.  %s", arg, error);
@@ -873,7 +873,7 @@ pm_parse_height(const char * const arg) {
     unsigned int height;
     const char * error;
 
-    pm_interpret_uint(arg, &height, &error);
+    pm_string_to_uint(arg, &height, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image height.  %s", arg, error);
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index 3378e076..27340d5f 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -23,376 +23,31 @@
 #include "netpbm/nstring.h"
 #include "ppm.h"
 #include "colorname.h"
+#include "pam.h"
 
 
-static void
-computeHexTable(int hexit[]) {
-
-    unsigned int i;
-
-    for ( i = 0; i < 256; ++i )
-        hexit[i] = -1;
-
-    hexit['0'] = 0;
-    hexit['1'] = 1;
-    hexit['2'] = 2;
-    hexit['3'] = 3;
-    hexit['4'] = 4;
-    hexit['5'] = 5;
-    hexit['6'] = 6;
-    hexit['7'] = 7;
-    hexit['8'] = 8;
-    hexit['9'] = 9;
-    hexit['a'] = hexit['A'] = 10;
-    hexit['b'] = hexit['B'] = 11;
-    hexit['c'] = hexit['C'] = 12;
-    hexit['d'] = hexit['D'] = 13;
-    hexit['e'] = hexit['E'] = 14;
-    hexit['f'] = hexit['F'] = 15;
-}
-
+pixel
+ppm_parsecolor2(const char * const colorname,
+                pixval       const maxval,
+                int          const closeOk) {
 
+    tuple const color = pnm_parsecolor2(colorname, maxval, closeOk);
 
-static long
-invRgbnorm(pixval       const rgb,
-           pixval       const maxval,
-           unsigned int const hexDigits) {
-/*----------------------------------------------------------------------------
-  This is the inverse of 'rgbnorm', below.
------------------------------------------------------------------------------*/
-    long retval;
-
-    switch (hexDigits) {
-    case 1:
-        retval = (long)((double) rgb * 15 / maxval + 0.5);
-        break;
-    case 2:
-        retval = (long) ((double) rgb * 255 / maxval + 0.5);
-        break;
-    case 3:
-        retval = (long) ((double) rgb * 4095 / maxval + 0.5);
-        break;
-    case 4:
-        retval = (long) ((double) rgb * 65535UL / maxval + 0.5);
-        break;
-    default:
-        pm_message("Internal error in invRgbnorm()");
-        abort();
-    }
-    return retval;
-}
-
+    pixel retval;
 
+    PPM_PUTR(retval, color[PAM_RED_PLANE]);
+    PPM_PUTG(retval, color[PAM_GRN_PLANE]);
+    PPM_PUTB(retval, color[PAM_BLU_PLANE]);
 
-static pixval
-rgbnorm(long         const rgb, 
-        pixval       const maxval, 
-        unsigned int const hexDigitCount, 
-        bool         const closeOk,
-        const char * const colorname) {
-/*----------------------------------------------------------------------------
-   Normalize the color (r, g, or b) value 'rgb', which was specified
-   with 'hexDigitCount' digits, to a maxval of 'maxval'.  If the
-   number of digits isn't valid, issue an error message and identify
-   the complete color color specification in error as 'colorname'.
-
-   For example, if the user says "0ff" and the maxval is 100,
-   then rgb is 0xff, n is 3, and our result is 
-   0xff / (16**3-1) * 100 = 6.
------------------------------------------------------------------------------*/
-    pixval retval;
-
-    switch (hexDigitCount) {
-    case 0:
-        pm_error("A hexadecimal color specifier in color '%s' is "
-                 "an empty string", colorname);
-        break;
-    case 1:
-        retval = (pixval)((double) rgb * maxval / 15 + 0.5);
-        break;
-    case 2:
-        retval = (pixval) ((double) rgb * maxval / 255 + 0.5);
-        break;
-    case 3:
-        retval = (pixval) ((double) rgb * maxval / 4095 + 0.5);
-        break;
-    case 4:
-        retval = (pixval) ((double) rgb * maxval / 65535L + 0.5);
-        break;
-    default:
-        pm_error("color specifier '%s' has too many digits", colorname);
-    }
+    free(color);
 
-    if (!closeOk) {
-        long const newrgb = invRgbnorm(retval, maxval, hexDigitCount);
-        if (newrgb != rgb)
-            pm_message("WARNING: Component 0x%lx of color '%s' "
-                       "cannot be represented precisely with maxval %u.  "
-                       "Approximating as %u.",
-                       rgb, colorname, maxval, retval);
-    }
     return retval;
 }
 
 
 
-static void
-parseHexDigits(const char *   const string,
-               char           const delim,
-               int            const hexit[],
-               pixval *       const nP,
-               unsigned int * const digitCountP) {
-
-    unsigned int digitCount;
-    pixval n;
-    
-    digitCount = 0;  /* initial value */
-    n = 0;           /* initial value */
-    while (string[digitCount] != delim) {
-        char const digit = string[digitCount];
-        if (digit == '\0')
-            pm_error("rgb: color spec ends prematurely");
-        else {
-            int const hexval = hexit[(unsigned int)digit];
-            if (hexval == -1)
-                pm_error("Invalid hex digit in rgb: color spec: 0x%02x",
-                         digit);
-            n = n * 16 + hexval;
-            ++digitCount;
-        }
-    }
-    *nP = n;
-    *digitCountP = digitCount;
-}
-
-
-
-static void
-parseNewHexX11(char       const colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-/*----------------------------------------------------------------------------
-   Determine what color colorname[] specifies in the new style hex
-   color specification format (e.g. rgb:55/40/55).
-
-   Return that color as *colorP.
-
-   Assume colorname[] starts with "rgb:", but otherwise it might be
-   gibberish.
------------------------------------------------------------------------------*/
-    int hexit[256];
-
-    const char * cp;
-    pixval n;
-    unsigned int digitCount;
-    pixval rNorm, gNorm, bNorm;
-
-    computeHexTable(hexit);
-
-    cp = &colorname[4];
-
-    parseHexDigits(cp, '/', hexit, &n, &digitCount);
-
-    rNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    cp += digitCount;
-    ++cp;  /* Skip the slash */
-
-    parseHexDigits(cp, '/', hexit, &n, &digitCount);
-
-    gNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    cp += digitCount;
-    ++cp;  /* Skip the slash */
-
-    parseHexDigits(cp, '\0', hexit, &n, &digitCount);
-
-    bNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-static void
-parseNewDecX11(char       const colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-
-    float const epsilon = 1.0/65536.0;
-    float fr, fg, fb;
-    pixval rNorm, gNorm, bNorm;
-
-    if (sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb) != 3)
-        pm_error("invalid color specifier '%s'", colorname);
-    if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 
-        || fb < 0.0 || fb > 1.0)
-        pm_error("invalid color specifier '%s' - "
-                 "values must be between 0.0 and 1.0", colorname );
-
-    rNorm = fr * maxval + 0.5;
-    gNorm = fg * maxval + 0.5;
-    bNorm = fb * maxval + 0.5;
-
-    if (!closeOk) {
-        if (fabs((double)rNorm/maxval - fr) > epsilon ||
-            fabs((double)gNorm/maxval - fg) > epsilon ||
-            fabs((double)bNorm/maxval - fb) > epsilon)
-            pm_message("WARNING: Color '%s' cannot be represented "
-                       "precisely with maxval %u.  "
-                       "Approximating as (%u,%u,%u).",
-                       colorname, maxval, rNorm, gNorm, bNorm);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-static void
-parseOldX11(char       const colorname[], 
-            pixval     const maxval,
-            bool       const closeOk,
-            pixel *    const colorP) {
-/*----------------------------------------------------------------------------
-   Return as *colorP the color specified by the old X11 style color
-   specififier colorname[] (e.g. #554055).
------------------------------------------------------------------------------*/
-    int hexit[256];
-    long r,g,b;
-    pixval rNorm, gNorm, bNorm;
-    
-    computeHexTable(hexit);
-
-    if (!pm_strishex(&colorname[1]))
-        pm_error("Non-hexadecimal characters in #-type color specification");
-
-    switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
-    case 3:
-        r = hexit[(int)colorname[1]];
-        g = hexit[(int)colorname[2]];
-        b = hexit[(int)colorname[3]];
-        rNorm = rgbnorm(r, maxval, 1, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 1, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 1, closeOk, colorname);
-        break;
-
-    case 6:
-        r = (hexit[(int)colorname[1]] << 4 ) + hexit[(int)colorname[2]];
-        g = (hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]];
-        b = (hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]];
-        rNorm = rgbnorm(r, maxval, 2, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 2, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 2, closeOk, colorname);
-        break;
-
-    case 9:
-        r = (hexit[(int)colorname[1]] << 8) +
-            (hexit[(int)colorname[2]] << 4) +
-            (hexit[(int)colorname[3]] << 0);
-        g = (hexit[(int)colorname[4]] << 8) + 
-            (hexit[(int)colorname[5]] << 4) +
-            (hexit[(int)colorname[6]] << 0);
-        b = (hexit[(int)colorname[7]] << 8) + 
-            (hexit[(int)colorname[8]] << 4) +
-            (hexit[(int)colorname[9]] << 0);
-        rNorm = rgbnorm(r, maxval, 3, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 3, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 3, closeOk, colorname);
-        break;
-
-    case 12:
-        r = (hexit[(int)colorname[1]] << 12) + 
-            (hexit[(int)colorname[2]] <<  8) +
-            (hexit[(int)colorname[3]] <<  4) + hexit[(int)colorname[4]];
-        g = (hexit[(int)colorname[5]] << 12) + 
-            (hexit[(int)colorname[6]] <<  8) +
-            (hexit[(int)colorname[7]] <<  4) + hexit[(int)colorname[8]];
-        b = (hexit[(int)colorname[9]] << 12) + 
-            (hexit[(int)colorname[10]] << 8) +
-            (hexit[(int)colorname[11]] << 4) + hexit[(int)colorname[12]];
-        rNorm = rgbnorm(r, maxval, 4, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 4, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 4, closeOk, colorname);
-        break;
-
-    default:
-        pm_error("invalid color specifier '%s'", colorname);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-
-static void
-parseOldX11Dec(const char       colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-
-    float const epsilon = 1.0/65536.0;
-
-    float fr, fg, fb;
-    pixval rNorm, gNorm, bNorm;
-
-    if (sscanf(colorname, "%f,%f,%f", &fr, &fg, &fb) != 3)
-        pm_error("invalid color specifier '%s'", colorname);
-    if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 
-        || fb < 0.0 || fb > 1.0)
-        pm_error("invalid color specifier '%s' - "
-                 "values must be between 0.0 and 1.0", colorname );
-
-    rNorm = fr * maxval + 0.5;
-    gNorm = fg * maxval + 0.5;
-    bNorm = fb * maxval + 0.5;
-
-    if (!closeOk) {
-        if (fabs((float)rNorm/maxval - fr) > epsilon ||
-            fabs((float)gNorm/maxval - fg) > epsilon ||
-            fabs((float)bNorm/maxval - fb) > epsilon)
-            pm_message("WARNING: Color '%s' cannot be represented "
-                       "precisely with maxval %u.  "
-                       "Approximating as (%u,%u,%u).",
-                       colorname, maxval, rNorm, gNorm, bNorm);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-pixel
-ppm_parsecolor2(const char * const colorname,
-                pixval       const maxval,
-                int          const closeOk) {
-
-    pixel color;
-    
-    if (strncmp(colorname, "rgb:", 4) == 0)
-        /* It's a new-X11-style hexadecimal rgb specifier. */
-        parseNewHexX11(colorname, maxval, closeOk, &color);
-    else if (strncmp(colorname, "rgbi:", 5) == 0)
-        /* It's a new-X11-style decimal/float rgb specifier. */
-        parseNewDecX11(colorname, maxval, closeOk, &color);
-    else if (colorname[0] == '#')
-        /* It's an old-X11-style hexadecimal rgb specifier. */
-        parseOldX11(colorname, maxval, closeOk, &color);
-    else if ((colorname[0] >= '0' && colorname[0] <= '9') ||
-             colorname[0] == '.')
-        /* It's an old-style decimal/float rgb specifier. */
-        parseOldX11Dec(colorname, maxval, closeOk, &color);
-    else 
-        /* Must be a name from the X-style rgb file. */
-        pm_parse_dictionary_name(colorname, maxval, closeOk, &color);
-    
-    return color;
-}
-
-
-
 pixel
-ppm_parsecolor(const char * const colorname, 
+ppm_parsecolor(const char * const colorname,
                pixval       const maxval) {
 
     return ppm_parsecolor2(colorname, maxval, TRUE);
@@ -401,13 +56,14 @@ ppm_parsecolor(const char * const colorname,
 
 
 char *
-ppm_colorname(const pixel * const colorP, 
-              pixval        const maxval, 
+ppm_colorname(const pixel * const colorP,
+              pixval        const maxval,
               int           const hexok)   {
 
     int r, g, b;
     FILE * f;
     static char colorname[200];
+        /* Null string means no suitable name so far */
 
     if (maxval == 255) {
         r = PPM_GETR(*colorP);
@@ -420,33 +76,57 @@ ppm_colorname(const pixel * const colorP,
     }
 
     f = pm_openColornameFile(NULL, !hexok);
-    if (f != NULL) {
-        int best_diff, this_diff;
+
+    if (!f)
+        STRSCPY(colorname, "");
+    else {
+        int bestDiff;
         bool eof;
 
-        best_diff = 32767;
-        eof = FALSE;
-        while (!eof && best_diff > 0 ) {
+        for (bestDiff = 32767, eof = FALSE;
+             !eof && bestDiff > 0; ) {
             struct colorfile_entry const ce = pm_colorget(f);
             if (ce.colorname)  {
-                this_diff = abs(r - ce.r) + abs(g - ce.g) + abs(b - ce.b);
-                if (this_diff < best_diff) {
-                    best_diff = this_diff;
-                    strcpy(colorname, ce.colorname);
+                int const thisDiff =
+                    abs(r - (int)ce.r) +
+                    abs(g - (int)ce.g) +
+                    abs(b - (int)ce.b);
+
+                if (thisDiff < bestDiff) {
+                    bestDiff = thisDiff;
+                    STRSCPY(colorname, ce.colorname);
                 }
             } else
                 eof = TRUE;
         }
         fclose(f);
-        if (best_diff != 32767 && (best_diff == 0 || ! hexok))
-            return colorname;
+
+        if (bestDiff == 32767) {
+            /* Color file contain no entries, so we can't even pick a close
+               one
+            */
+            STRSCPY(colorname, "");
+        } else if (bestDiff > 0 && hexok) {
+            /* We didn't find an exact match and user is willing to accept
+               hex, so we don't have to use an approximate match.
+            */
+            STRSCPY(colorname, "");
+        }
     }
 
-    /* Color lookup failed, but caller is willing to take an X11-style
-       hex specifier, so return that.
-    */
-    sprintf(colorname, "#%02x%02x%02x", r, g, b);
-    return colorname;}
+    if (streq(colorname, "")) {
+        if (hexok) {
+            /* Color lookup failed, but caller is willing to take an X11-style
+               hex specifier, so return that.
+            */
+            sprintf(colorname, "#%02x%02x%02x", r, g, b);
+        } else {
+            pm_error("Couldn't find any name colors at all");
+        }
+    }
+
+    return colorname;
+}
 
 
 
@@ -509,7 +189,7 @@ processColorfileEntry(struct colorfile_entry const ce,
             /* The color is already in the hash, which means we saw it
                earlier in the file.  We prefer the first name that the
                file gives for each color, so we just ignore the
-               current entry.  
+               current entry.
             */
             *errorP = NULL;
         } else {
@@ -576,15 +256,15 @@ readOpenColorFile(FILE *          const colorFileP,
 
     while (!done && !*errorP) {
         struct colorfile_entry const ce = pm_colorget(colorFileP);
-        
-        if (!ce.colorname)  
+
+        if (!ce.colorname)
             done = TRUE;
-        else 
+        else
             processColorfileEntry(ce, cht, colornames, colors,
                                   &nColorsDone, errorP);
     }
     *nColorsP = nColorsDone;
-    
+
     if (*errorP) {
         unsigned int colorIndex;
 
@@ -630,13 +310,13 @@ readColorFile(const char *    const fileName,
         } else {
             readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht,
                               errorP);
-            
+
             fclose(colorFileP);
         }
     }
 }
 
-    
+
 
 static void
 readcolordict(const char *      const fileName,
@@ -657,14 +337,14 @@ readcolordict(const char *      const fileName,
         pixel * colors;
 
         MALLOCARRAY(colors, MAXCOLORNAMES);
-        
+
         if (colors == NULL)
             pm_asprintf(errorP, "Unable to allocate space for color table.");
         else {
             colorhash_table cht;
 
             cht = allocColorHash();
-            
+
             if (cht == NULL)
                 pm_asprintf(errorP, "Unable to allocate space for color hash");
             else {
@@ -754,9 +434,9 @@ ppm_readcolordict(const char *      const fileName,
 
 
 void
-ppm_readcolornamefile(const char *      const fileName, 
+ppm_readcolornamefile(const char *      const fileName,
                       int               const mustOpen,
-                      colorhash_table * const chtP, 
+                      colorhash_table * const chtP,
                       const char ***    const colornamesP) {
 
     ppm_readcolordict(fileName, mustOpen, NULL, colornamesP, NULL, chtP);
@@ -778,7 +458,7 @@ ppm_freecolornames(const char ** const colornames) {
 
 
 
-static unsigned int 
+static unsigned int
 nonnegative(unsigned int const arg) {
 
     if ((int)(arg) < 0)
@@ -790,8 +470,8 @@ nonnegative(unsigned int const arg) {
 
 
 pixel
-ppm_color_from_ycbcr(unsigned int const y, 
-                     int          const cb, 
+ppm_color_from_ycbcr(unsigned int const y,
+                     int          const cb,
                      int          const cr) {
 /*----------------------------------------------------------------------------
    Return the color that has luminance 'y', blue chrominance 'cb', and
@@ -805,12 +485,12 @@ ppm_color_from_ycbcr(unsigned int const y,
 -----------------------------------------------------------------------------*/
     pixel retval;
 
-    PPM_ASSIGN(retval, 
+    PPM_ASSIGN(retval,
                y + 1.4022 * cr,
                nonnegative(y - 0.7145 * cr - 0.3456 * cb),
                y + 1.7710 * cb
         );
-    
+
     return retval;
 }
 
@@ -876,7 +556,7 @@ ppm_color_from_hsv(struct hsv const hsv,
             pm_error("Invalid H value passed to color_from_HSV: %f", hsv.h);
         }
     }
-    PPM_ASSIGN(retval, 
+    PPM_ASSIGN(retval,
                ROUNDU(R * maxval),
                ROUNDU(G * maxval),
                ROUNDU(B * maxval));
diff --git a/lib/libsystem.c b/lib/libsystem.c
index 57073dce..bf2416a4 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -18,7 +18,6 @@
    Contributed to the public domain.
 =============================================================================*/
 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
-#define _XOPEN_SOURCE
 #define _BSD_SOURCE  /* Make SIGWINCH defined on OpenBSD */
 
 #include <stdarg.h>
diff --git a/lib/pam.h b/lib/pam.h
index d8d0e5c7..c2cfb4c7 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -401,6 +401,11 @@ typedef float samplen;
 typedef samplen *tuplen;
     /* Same as 'tuple', except using normalized samples. */
 
+tuplen
+pnm_allocpamtuplen(const struct pam * const pamP);
+
+#define pnm_freepamtuplen(tuplen) pm_freerow((char*) tuplen)
+
 tuplen *
 pnm_allocpamrown(const struct pam * const pamP);
 
@@ -490,6 +495,11 @@ pnm_createungammatransform(const struct pam * const pamP);
 #define pnm_freeungammatransform pnm_freegammatransform;
 
 tuple
+pnm_parsecolor2(const char * const colorname,
+                sample       const maxval,
+                int          const closeOk);
+
+tuple
 pnm_parsecolor(const char * const colorname,
                sample       const maxval);
 
@@ -501,6 +511,29 @@ pnm_colorname(struct pam * const pamP,
               tuple        const color,
               int          const hexok);
 
+const char *
+pnm_colorspec_rgb_integer(struct pam * const pamP,
+                          tuple        const color,
+                          sample       const maxval);
+
+const char *
+pnm_colorspec_rgb_norm(struct pam * const pamP,
+                       tuple        const color,
+                       unsigned int const digitCt);
+
+const char *
+pnm_colorspec_rgb_x11(struct pam * const pamP,
+                      tuple        const color,
+                      unsigned int const hexDigitCt);
+
+const char *
+pnm_colorspec_dict(struct pam * const pamP,
+                   tuple        const color);
+
+const char *
+pnm_colorspec_dict_close(struct pam * const pamP,
+                         tuple        const color);
+
 extern double
 pnm_lumin_factor[3];
 
diff --git a/lib/util/mallocvar.c b/lib/util/mallocvar.c
index 339d5d61..f75bd94d 100644
--- a/lib/util/mallocvar.c
+++ b/lib/util/mallocvar.c
@@ -30,7 +30,7 @@ allocarrayNoHeap(void **      const rowIndex,
         void * rowSpace;
 
         mallocProduct(&rowSpace, cols, elementSize);
-        
+
         if (rowSpace == NULL) {
             unsigned int row;
 
@@ -123,7 +123,7 @@ pm_mallocarray2(void **      const resultP,
 
             if (rowheap) {
                 unsigned int row;
-                
+
                 for (row = 0; row < rows; ++row)
                     rowIndex[row] = &(rowheap[row * cols * elementSize]);
             }
@@ -133,7 +133,7 @@ pm_mallocarray2(void **      const resultP,
                format.
             */
             rowIndex[rows+1] = NULL;   /* Declare it fragmented format */
-            
+
             allocarrayNoHeap(rowIndex, rows, cols, elementSize, &failed);
         }
         rowIndex[rows+0] = NULL;   /* mark end of rows */
@@ -151,7 +151,7 @@ array2RowCount(void ** const rowIndex) {
 /*----------------------------------------------------------------------------
    Return the number of rows in the 2-dimensional array.
 -----------------------------------------------------------------------------*/
-    /* The end of the rows is marked by a null pointer where a row 
+    /* The end of the rows is marked by a null pointer where a row
        pointer otherwise would be.
     */
 
diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h
index e92e3fe4..00ba6484 100644
--- a/lib/util/mallocvar.h
+++ b/lib/util/mallocvar.h
@@ -1,5 +1,5 @@
 /* These are some dynamic memory allocation facilities.  They are essentially
-   an extension to C, as they do allocations with a cognizance of C 
+   an extension to C, as they do allocations with a cognizance of C
    variables.  You can use them to make C read more like a high level
    language.
 
@@ -23,7 +23,7 @@ extern "C" {
 #endif
 
 static __inline__ void
-mallocProduct(void **      const resultP, 
+mallocProduct(void **      const resultP,
               unsigned int const factor1,
               unsigned int const factor2) {
 /*----------------------------------------------------------------------------
@@ -44,10 +44,10 @@ mallocProduct(void **      const resultP,
     if (factor1 == 0 || factor2 == 0)
         *resultP = malloc(1);
     else {
-        if (UINT_MAX / factor2 < factor1) 
+        if (UINT_MAX / factor2 < factor1)
             *resultP = NULL;
-        else 
-            *resultP = malloc(factor1 * factor2); 
+        else
+            *resultP = malloc(factor1 * factor2);
     }
 }
 
@@ -61,11 +61,11 @@ reallocProduct(void **      const blockP,
     void * const oldBlockP = *blockP;
 
     void * newBlockP;
-    
-    if (UINT_MAX / factor2 < factor1) 
+
+    if (UINT_MAX / factor2 < factor1)
         newBlockP = NULL;
-    else 
-        newBlockP = realloc(oldBlockP, factor1 * factor2); 
+    else
+        newBlockP = realloc(oldBlockP, factor1 * factor2);
 
     if (newBlockP)
         *blockP = newBlockP;
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index 8cff4d56..7ef9fcfb 100644
--- a/lib/util/nstring.c
+++ b/lib/util/nstring.c
@@ -1007,43 +1007,6 @@ pm_strishex(const char * const subject) {
 
 
 void
-pm_interpret_uint(const char *   const string,
-                  unsigned int * const valueP,
-                  const char **  const errorP) {
-
-    if (string[0] == '\0')
-        pm_asprintf(errorP, "Null string.");
-    else {
-        /* strtoul() does a bizarre thing where if the number is out
-           of range, it returns a clamped value but tells you about it
-           by setting errno = ERANGE.  If it is not out of range,
-           strtoul() leaves errno alone.
-        */
-        char * tail;
-        unsigned long ulongValue;
-
-        errno = 0;  /* So we can tell if strtoul() overflowed */
-
-        ulongValue = strtoul(string, &tail, 10);
-
-        if (tail[0] != '\0')
-            pm_asprintf(errorP, "Non-digit stuff in string: %s", tail);
-        else if (errno == ERANGE)
-            pm_asprintf(errorP, "Number too large");
-        else if (ulongValue > UINT_MAX)
-            pm_asprintf(errorP, "Number too large");
-        else if (string[0] == '-')
-            pm_asprintf(errorP, "Negative number");
-            /* Sleazy code; string may have leading spaces. */
-        else {
-            *valueP = ulongValue;
-            *errorP = NULL;
-        }
-    }
-}
-
-
-void
 pm_string_to_uint(const char *   const string,
                   unsigned int * const uintP,
                   const char **  const errorP) {
@@ -1052,31 +1015,39 @@ pm_string_to_uint(const char *   const string,
         pm_asprintf(errorP, "Value is a null string");
     else {
         char * tailptr;
+        long longValue;
 
-        /* We can't use 'strtoull'.  Contrary to expectations, though as
+        /* We can't use 'strtoul'.  Contrary to expectations, though as
            designed, it returns junk if there is a minus sign.
         */
 
-        long longValue;
+        /* strtol() does a bizarre thing where if the number is out
+           of range, it returns a clamped value but tells you about it
+           by setting errno = ERANGE.  If it is not out of range,
+           strtol() leaves errno alone.
+        */
+        errno = 0;  /* So we can tell if strtoul() overflowed */
 
         longValue = strtol(string, &tailptr, 10);
 
-
-        *uintP = strtoul(string, &tailptr, 10);
-
         if (*tailptr != '\0')
             pm_asprintf(errorP, "Non-numeric crap in string: '%s'", tailptr);
         else {
-            if (longValue < 0)
-                pm_asprintf(errorP, "Number is negative");
-            else {
-                if ((unsigned int)longValue != longValue)
-                    pm_asprintf(errorP, "Number is too large for computation");
-                else {
-                    *uintP = (unsigned int)longValue;
-                    *errorP = NULL;
-                }
-            }
+             if (errno == ERANGE)
+                 pm_asprintf(errorP, "Number is too large for computation");
+             else {
+                 if (longValue < 0)
+                     pm_asprintf(errorP, "Number is negative");
+                 else {
+                     if ((unsigned int)longValue != longValue)
+                         pm_asprintf(errorP,
+                                     "Number is too large for computation");
+                     else {
+                         *uintP = (unsigned int)longValue;
+                         *errorP = NULL;
+                     }
+                 }
+             }
         }
     }
 }
diff --git a/lib/util/nstring.h b/lib/util/nstring.h
index bded8417..5159277c 100644
--- a/lib/util/nstring.h
+++ b/lib/util/nstring.h
@@ -60,15 +60,21 @@ static __inline__ int
 memeq(const void * const comparand,
       const void * const comparator,
       size_t       const size) {
-    
+
     return memcmp(comparand, comparator, size) == 0;
 }
 
-/* The Standard C Library may not declare strcasecmp() if the including
-   source file doesn't request BSD functions, with _BSD_SOURCE.  So
-   we don't define functions that use strcasecmp() in that case.
+/* The Standard C Library may not declare strcasecmp() if the including source
+   file doesn't request BSD functions, with _BSD_SOURCE or SUSv2 function,
+   with _XOPEN_SOURCE >= 500.  So we don't define functions that use
+   strcasecmp() in that case.
+
+   (Actually, _XOPEN_SOURCE 500 is stronger than you need for strcasecmp -
+   _XOPEN_SOURCE_EXTENDED, which asks for XPG 4, would do, whereas
+   _XOPEN_SOURCE 500 asks for XPG 5, but for simplicity, we don't use
+   _XOPEN_SOURCE_EXTENDED in Netpbm.
 */
-#ifdef _BSD_SOURCE
+#if defined(_BSD_SOURCE) || (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE - 0) >= 500)
 static __inline__ int
 strcaseeq(const char * const comparand,
           const char * const comparator) {
@@ -86,7 +92,7 @@ strncaseeq(const char * const comparand,
 #endif
 
 
-/* The standard C library routines isdigit(), for some weird 
+/* The standard C library routines isdigit(), for some weird
    historical reason, does not take a character (type 'char') as its
    argument.  Instead it takes an integer.  When the integer is a whole
    number, it represents a character in the obvious way using the local
@@ -126,7 +132,7 @@ strncaseeq(const char * const comparand,
    Netpbm must include them in its own libraries, and because some
    standard C libraries have some of them, Netpbm must use different
    names for them.
-   
+
    The GNU C library has all of them.  All but the oldest standard C libraries
    have snprintf().
 
@@ -183,7 +189,7 @@ pm_vasprintf(const char ** const resultP,
 bool
 pm_vasprintf_knows_float(void);
 
-void 
+void
 pm_strfree(const char * const string);
 
 const char *
@@ -203,11 +209,6 @@ bool
 pm_strishex(const char * const subject);
 
 void
-pm_interpret_uint(const char *   const string,
-               unsigned int * const valueP,
-               const char **  const errorP);
-
-void
 pm_string_to_uint(const char *   const string,
                   unsigned int * const uintP,
                   const char **  const errorP);