about summary refs log tree commit diff
path: root/other
diff options
context:
space:
mode:
Diffstat (limited to 'other')
-rw-r--r--other/Makefile17
-rw-r--r--other/pamarith.c97
-rw-r--r--other/pambayer.c90
-rw-r--r--other/pamchannel.c25
-rw-r--r--other/pamdepth.c13
-rw-r--r--other/pamexec.c192
-rw-r--r--other/pamfix.c291
-rwxr-xr-xother/pamfixtrunc50
-rw-r--r--other/pamfixtrunc.c176
-rw-r--r--other/pamlookup.c186
-rw-r--r--other/pampick.c10
-rw-r--r--other/pamsplit.c23
-rw-r--r--other/pamstack.c11
-rw-r--r--other/pamsummcol.c2
-rw-r--r--other/pamunlookup.c250
-rw-r--r--other/pamvalidate.c86
-rw-r--r--other/pamx/Makefile48
-rw-r--r--other/pamx/pamx.c7
-rw-r--r--other/pamx/send.c6
-rw-r--r--other/pamx/window.c27
-rw-r--r--other/pnmcolormap.c8
-rw-r--r--other/ppmdcfont.c20
-rw-r--r--other/ppmsvgalib.c16
23 files changed, 1269 insertions, 382 deletions
diff --git a/other/Makefile b/other/Makefile
index f7333c64..bd2c9dc2 100644
--- a/other/Makefile
+++ b/other/Makefile
@@ -24,17 +24,17 @@ endif
 # build.
 
 PORTBINARIES = pamarith pambayer pamchannel pamdepth \
-	pamendian pamfixtrunc pamlookup pampick pamsplit \
-	pamstack pamsummcol pnmcolormap \
+	pamendian pamexec pamfix pamlookup pampick pamsplit \
+	pamstack pamsummcol pamunlookup pamvalidate pnmcolormap \
 	ppmdcfont ppmddumpfont ppmdmkfont 
 
-BINARIES = $(PORTBINARIES)
-
 ifneq ($(LINUXSVGALIB),NONE)
-  BINARIES += ppmsvgalib
+  PORTBINARIES += ppmsvgalib
 endif
 
-SCRIPTS = ppmtomap
+BINARIES = $(PORTBINARIES)
+
+SCRIPTS = ppmtomap pamfixtrunc
 
 OBJECTS = $(BINARIES:%=%.o)
 
@@ -50,10 +50,7 @@ all: $(BINARIES) $(SUBDIRS:%=%/all)
 
 include $(SRCDIR)/common.mk
 
-ppmsvgalib: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $< \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(LINUXSVGALIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(LADD) 
+ppmsvgalib: LDFLAGS_TARGET = $(shell $(LIBOPT) $(LINUXSVGALIB))
 
 install.bin: install.bin.local
 .PHONY: install.bin.local
diff --git a/other/pamarith.c b/other/pamarith.c
index 3d29ac93..54c18485 100644
--- a/other/pamarith.c
+++ b/other/pamarith.c
@@ -47,7 +47,7 @@ isDyadic(enum function const function) {
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -60,7 +60,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -102,7 +102,7 @@ parseCommandLine(int argc, const char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (addSpec + subtractSpec + multiplySpec + divideSpec + differenceSpec +
@@ -400,48 +400,66 @@ doNormalizedArith(struct pam *  const inpam1P,
                   struct pam *  const outpamP,
                   enum function const function) {
 
+    /* Some of the logic in this subroutine is designed for future
+       expansion into non-dyadic computations.  But for now, all
+       computations have exactly two operands.
+    */
     unsigned int const operandCt = 2;
 
-    tuplen * tuplerown1;
-    tuplen * tuplerown2;
+    tuplen ** tuplerown;
+        /* tuplerown[0] is the current row in the first operand image */
+
     tuplen * tuplerownOut;
     unsigned int row;
     samplen * operands;
+        /* operand[0] is the first operand in the current one-sample
+           computation
+        */
+    unsigned int * plane;
+        /* plane[0] is the plane number in the first operand image for 
+           the current one-sample computation.  plane[1] is the plane number
+           in the second operand image, etc.
+         */
 
     MALLOCARRAY_NOFAIL(operands, operandCt);
+    MALLOCARRAY_NOFAIL(plane, operandCt);
+    MALLOCARRAY_NOFAIL(tuplerown, operandCt);
 
-    tuplerown1   = pnm_allocpamrown(inpam1P);
-    tuplerown2   = pnm_allocpamrown(inpam2P);
+    tuplerown[0] = pnm_allocpamrown(inpam1P);
+    tuplerown[1] = pnm_allocpamrown(inpam2P);
     tuplerownOut = pnm_allocpamrown(outpamP);
 
     for (row = 0; row < outpamP->height; ++row) {
         unsigned int col;
-        pnm_readpamrown(inpam1P, tuplerown1);
-        pnm_readpamrown(inpam2P, tuplerown2);
+        pnm_readpamrown(inpam1P, tuplerown[0]);
+        pnm_readpamrown(inpam2P, tuplerown[1]);
         
         for (col = 0; col < outpamP->width; ++col) {
             unsigned int outplane;
             
             for (outplane = 0; outplane < outpamP->depth; ++outplane) {
-                unsigned int const plane1 = MIN(outplane, inpam1P->depth-1);
-                unsigned int const plane2 = MIN(outplane, inpam2P->depth-1);
+                unsigned int op;
 
-                operands[0] = tuplerown1[col][plane1];
-                operands[1] = tuplerown2[col][plane2];
+                plane[0] = MIN(outplane, inpam1P->depth-1);
+                plane[1] = MIN(outplane, inpam2P->depth-1);
+
+                for (op = 0; op < operandCt; ++op)
+                    operands[op] = tuplerown[op][col][plane[op]];
 
                 tuplerownOut[col][outplane] = 
                     applyNormalizedFunction(function, operands, operandCt); 
                 assert(tuplerownOut[col][outplane] >= 0.);
                 assert(tuplerownOut[col][outplane] <= 1.);
-
             }
         }
         pnm_writepamrown(outpamP, tuplerownOut);
     }
 
-    pnm_freepamrown(tuplerown1);
-    pnm_freepamrown(tuplerown2);
+    pnm_freepamrown(tuplerown[0]);
+    pnm_freepamrown(tuplerown[1]);
+    free(tuplerown);
     pnm_freepamrown(tuplerownOut);
+    free(plane);
     free(operands);
 }
 
@@ -701,14 +719,28 @@ doUnNormalizedArith(struct pam *  const inpam1P,
    maxval to do the computation without time-consuming normalization of
    sample values.
 -----------------------------------------------------------------------------*/
+    /* Some of the logic in this subroutine is designed for future
+       expansion into non-dyadic computations.  But for now, all
+       computations have exactly two operands.
+    */
     unsigned int const operandCt = 2;
+
     sample const maxval = outpamP->maxval;
 
-    tuple * tuplerow1;
-    tuple * tuplerow2;
+    tuple ** tuplerow;
+        /* tuplerow[0] is the current row in the first operand image */
+
     tuple * tuplerowOut;
     unsigned int row;
     sample * operands;
+        /* operand[0] is the first operand in the current one-sample
+           computation
+        */
+    unsigned int * plane;
+        /* plane[0] is the plane number in the first operand image for 
+           the current one-sample computation.  plane[1] is the plane number
+           in the second operand image, etc.
+         */
 
     /* Input conditions: */
     assert(inpam1P->maxval == maxval);
@@ -716,25 +748,29 @@ doUnNormalizedArith(struct pam *  const inpam1P,
     assert(outpamP->maxval == maxval);
 
     MALLOCARRAY_NOFAIL(operands, operandCt);
+    MALLOCARRAY_NOFAIL(plane, operandCt);
+    MALLOCARRAY_NOFAIL(tuplerow, operandCt);
 
-    tuplerow1   = pnm_allocpamrow(inpam1P);
-    tuplerow2   = pnm_allocpamrow(inpam2P);
+    tuplerow[0]   = pnm_allocpamrow(inpam1P);
+    tuplerow[1]   = pnm_allocpamrow(inpam2P);
     tuplerowOut = pnm_allocpamrow(outpamP);
 
     for (row = 0; row < outpamP->height; ++row) {
         unsigned int col;
-        pnm_readpamrow(inpam1P, tuplerow1);
-        pnm_readpamrow(inpam2P, tuplerow2);
+        pnm_readpamrow(inpam1P, tuplerow[0]);
+        pnm_readpamrow(inpam2P, tuplerow[1]);
         
         for (col = 0; col < outpamP->width; ++col) {
             unsigned int outplane;
             
             for (outplane = 0; outplane < outpamP->depth; ++outplane) {
-                unsigned int const plane1 = MIN(outplane, inpam1P->depth-1);
-                unsigned int const plane2 = MIN(outplane, inpam2P->depth-1);
+                unsigned int op;
+
+                plane[0] = MIN(outplane, inpam1P->depth-1);
+                plane[1] = MIN(outplane, inpam2P->depth-1);
 
-                operands[0] = tuplerow1[col][plane1];
-                operands[1] = tuplerow2[col][plane2];
+                for (op = 0; op < operandCt; ++op)
+                    operands[op] = tuplerow[op][col][plane[op]];
 
                 tuplerowOut[col][outplane] = 
                     applyUnNormalizedFunction(function, operands, operandCt,
@@ -747,10 +783,11 @@ doUnNormalizedArith(struct pam *  const inpam1P,
         pnm_writepamrow(outpamP, tuplerowOut);
     }
 
-    pnm_freepamrow(tuplerow1);
-    pnm_freepamrow(tuplerow2);
+    pnm_freepamrow(tuplerow[0]);
+    pnm_freepamrow(tuplerow[1]);
+    free(tuplerow);
     pnm_freepamrow(tuplerowOut);
-
+    free(plane);
     free(operands);
 }
 
@@ -759,7 +796,7 @@ doUnNormalizedArith(struct pam *  const inpam1P,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam inpam1;
     struct pam inpam2;
     struct pam outpam;
diff --git a/other/pambayer.c b/other/pambayer.c
index f8ce0db8..7fc1f809 100644
--- a/other/pambayer.c
+++ b/other/pambayer.c
@@ -42,19 +42,20 @@ enum bayerType {
 struct cmdlineInfo {
     const char * inputFilespec;
     enum bayerType bayerType;
+    unsigned int nointerpolate;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -67,12 +68,14 @@ parseCommandLine(int argc, char ** argv,
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "type",     OPT_UINT, &type,
             &typeSpec, 0);
+    OPTENT3(0, "nointerpolate", OPT_FLAG, NULL,
+            &cmdlineP->nointerpolate, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -102,28 +105,47 @@ calc_4(const struct pam * const pamP,
        tuple **           const intuples,
        tuple **           const outtuples,
        unsigned int       const plane,
+       bool               const noInterpolation,
        unsigned int       const xoffset,
        unsigned int       const yoffset) {
 /*----------------------------------------------------------------------------
     X . X
     . . .
     X . X
+
+  For the Plane 'plane' sample values, an even pixel of outtuples[] gets the
+  same value as intuples[][].  An odd pixel of outtuples[] gets the mean of
+  the four surrounding even pixels, north, south, east, and west.  But zero if
+  Caller says 'noInterpolation'.
+
+  (even/odd is with respect to ('xoffset', 'yoffset')).
 -----------------------------------------------------------------------------*/
-    unsigned int y;
+    unsigned int row;
     
-    for (y = yoffset; y < pamP->height; y += 2) {
-        unsigned int x;
-        for (x = xoffset; x + 2 < pamP->width; x += 2) {
-            outtuples[y][x][plane] = intuples[y][x][0];
-            outtuples[y][x + 1][plane] =
-                (intuples[y][x][0] + intuples[y][x + 2][0]) / 2;
+    /* Do the even rows -- the even column pixels get copied from the input,
+       while the odd column pixels get the mean of adjacent even ones
+    */
+    for (row = yoffset; row < pamP->height; row += 2) {
+        unsigned int col;
+        for (col = xoffset; col + 2 < pamP->width; col += 2) {
+            outtuples[row][col][plane] = intuples[row][col][0];
+            outtuples[row][col + 1][plane] =
+                noInterpolation ?
+                0 :
+                (intuples[row][col][0] + intuples[row][col + 2][0]) / 2;
         }
     }
-    for (y = yoffset; y + 2 < pamP->height; y += 2) {
-        unsigned int x;
-        for (x = xoffset; x < pamP->width; ++x)
-            outtuples[y + 1][x][plane] =
-                (outtuples[y][x][plane] + outtuples[y + 2][x][plane]) / 2;
+
+    /* Do the odd rows -- every pixel is the mean of the one above and below */
+    for (row = yoffset; row + 2 < pamP->height; row += 2) {
+        unsigned int col;
+        for (col = xoffset; col < pamP->width; ++col) {
+            outtuples[row + 1][col][plane] = 
+                noInterpolation ?
+                0 :
+                (outtuples[row][col][plane] +
+                 outtuples[row + 2][col][plane]) / 2;
+        }
     }
 }
 
@@ -134,25 +156,37 @@ calc_5(const struct pam * const pamP,
        tuple **           const intuples,
        tuple **           const outtuples,
        unsigned int       const plane,
+       bool               const noInterpolation,
        unsigned int       const xoffset,
        unsigned int       const yoffset) {
 /*----------------------------------------------------------------------------
    . X .
    X . X
    . X .
+
+  For the Plane 'plane' sample values, an pixel on an even diagonal of
+  outtuples[] gets the same value as intuples[][].  An pixel on an odd
+  diagonal gets the mean of the four surrounding even pixels, north,
+  south, east, and west.  But zero if Caller says 'noInterpolation'.
+
+  (even/odd is with respect to ('xoffset', 'yoffset')).
 -----------------------------------------------------------------------------*/
-    unsigned int y;
+    unsigned int row;
     unsigned int j;
 
     j = 0;  /* initial value */
 
-    for (y = yoffset; y + 2 < pamP->height; ++y) {
-        unsigned int x;
-        for (x = xoffset + j; x + 2 < pamP->width; x += 2) {
-            outtuples[y][x + 1][plane] = intuples[y][x + 1][0];
-            outtuples[y + 1][x + 1][plane] = 
-                (intuples[y][x + 1][0] + intuples[y + 1][x][0] +
-                 intuples[y + 2][x + 1][0] + intuples[y + 1][x + 2][0]) / 4;
+    for (row = yoffset; row + 2 < pamP->height; ++row) {
+        unsigned int col;
+        for (col = xoffset + j; col + 2 < pamP->width; col += 2) {
+            outtuples[row][col + 1][plane] = intuples[row][col + 1][0];
+            outtuples[row + 1][col + 1][plane] =
+                noInterpolation ?
+                0 :
+                (intuples[row][col + 1][0] +
+                 intuples[row + 1][col][0] +
+                 intuples[row + 2][col + 1][0] +
+                 intuples[row + 1][col + 2][0]) / 4;
         }
         j = 1 - j;
     }
@@ -167,6 +201,7 @@ struct compAction {
                  tuple **           const intuples,
                  tuple **           const outtuples,
                  unsigned int       const plane,
+                 bool               const noInterpolation,
                  unsigned int       const xoffset,
                  unsigned int       const yoffset);
 };
@@ -260,7 +295,7 @@ actionTableForType(enum bayerType const bayerType) {
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
@@ -271,8 +306,8 @@ main(int argc, char **argv) {
     const struct compAction * compActionTable;
     unsigned int plane;
 
-    pnm_init(&argc, argv);
-
+    pm_proginit(&argc, argv);
+    
     parseCommandLine(argc, argv, &cmdline);
     
     ifP = pm_openr(cmdline.inputFilespec);
@@ -289,6 +324,7 @@ main(int argc, char **argv) {
         struct compAction const compAction = compActionTable[plane];
 
         compAction.calc(&inpam, intuples, outtuples, plane,
+                        cmdline.nointerpolate,
                         compAction.xoffset, compAction.yoffset);
     }
     pnm_writepam(&outpam, outtuples);
diff --git a/other/pamchannel.c b/other/pamchannel.c
index 64ab728b..e89a979b 100644
--- a/other/pamchannel.c
+++ b/other/pamchannel.c
@@ -14,14 +14,14 @@
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 #define MAX_CHANNELS 16
     /* The most channels we allow user to specify */
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -36,14 +36,14 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
     extern struct pam pam;  /* Just so we can look at field sizes */
@@ -63,7 +63,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!infileSpec)
@@ -74,7 +74,8 @@ parseCommandLine(int argc, char ** argv,
     else
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("Tuple type name specified is too long.  Maximum of "
-                     "%u characters allowed.", sizeof(pam.tuple_type));
+                     "%u characters allowed.",
+                     (unsigned)sizeof(pam.tuple_type));
 
     cmdlineP->n_channel = 0;  /* initial value */
     { 
@@ -173,13 +174,13 @@ doOneImage(FILE *       const ifP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
-    bool eof;
+    int eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
diff --git a/other/pamdepth.c b/other/pamdepth.c
index ee59a408..71dae9d8 100644
--- a/other/pamdepth.c
+++ b/other/pamdepth.c
@@ -28,14 +28,14 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec strings we return are stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -51,7 +51,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -133,16 +133,15 @@ transformRaster(struct pam * const inpamP,
 
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
     struct pam inpam;
     struct pam outpam;
-    bool eof;
+    int eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/other/pamexec.c b/other/pamexec.c
new file mode 100644
index 00000000..d14d8752
--- /dev/null
+++ b/other/pamexec.c
@@ -0,0 +1,192 @@
+/******************************************************************************
+                               pamexec
+*******************************************************************************
+  Split a Netpbm format input file into multiple Netpbm format output streams
+  with one image per output stream and pipe this into the specified command.
+
+  By Bryan Henderson, Olympia WA; June 2000
+  and Michael Pot, New Zealand, August 2011
+
+  Contributed to the public domain by its authors.
+
+******************************************************************************/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "pam.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * command;
+    const char * inputFileName;
+    unsigned int debug;
+    unsigned int check;
+};
+
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the pointers we place into *cmdlineP are sometimes to storage
+   in the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "debug",   OPT_FLAG,   NULL,         &cmdlineP->debug, 0);
+    OPTENT3(0,   "check",   OPT_FLAG,   NULL,         &cmdlineP->check, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1) 
+        pm_error("You must specify at least one argument: the shell command "
+                 "to execute");
+    else {
+        cmdlineP->command = argv[1];
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  There are at most two: "
+                         "command and input file name");
+        }
+    }
+}
+
+
+
+static void
+pipeOneImage(FILE * const infileP,
+             FILE * const outfileP) {
+
+    struct pam inpam;
+    struct pam outpam;
+    enum pm_check_code checkRetval;
+    
+    unsigned int row;
+    tuple * tuplerow;
+
+    pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval);
+
+    outpam = inpam;
+    outpam.file = outfileP;
+
+    pnm_writepaminit(&outpam);
+
+    tuplerow = pnm_allocpamrow(&inpam);
+
+    for (row = 0; row < inpam.height; ++row) {
+        pnm_readpamrow(&inpam, tuplerow);
+        pnm_writepamrow(&outpam, tuplerow);
+    }
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+doOneImage(FILE *        const ifP,
+           const char *  const command,
+           bool          const check,
+           const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Run command 'command' on the next image in stream *ifP.
+
+   Return as *errorP a text explanation of how we failed, or NULL if we
+   didn't.
+-----------------------------------------------------------------------------*/
+    FILE * ofP;
+
+    ofP = popen(command, "w");
+
+    if (ofP == NULL)
+        pm_asprintf(errorP, 
+                    "Failed to start shell to run command '%s'.  "
+                    "errno = %d (%s)",
+                    command, errno, strerror(errno));
+    else {
+        int rc;
+
+        pipeOneImage(ifP, ofP);
+            
+        rc = pclose(ofP);
+
+        if (check && rc != 0)
+            pm_asprintf(errorP, "Command '%s' terminated abnormally "
+                        "or with nonzero exit status", command);
+        else
+            *errorP = NULL;
+    }
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+
+    FILE *       ifP;         /* Input file pointer */
+    int          eof;         /* No more images in input */
+    unsigned int imageSeq;
+        /* Sequence number of current image in input file.  First = 0.
+           (Useful for tracking down problems).
+        */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    
+    ifP = pm_openr(cmdline.inputFileName);
+
+    for (eof = FALSE, imageSeq = 0; !eof; ++imageSeq) {
+        const char * error;
+        
+        doOneImage(ifP, cmdline.command, cmdline.check, &error);
+
+        if (error) {
+            pm_error("Failed on image %u: %s", imageSeq, error);
+            pm_strfree(error);
+        }
+
+        pnm_nextimage(ifP, &eof);
+    }
+    pm_close(ifP);
+    
+    return 0;
+}
+
+
+
diff --git a/other/pamfix.c b/other/pamfix.c
new file mode 100644
index 00000000..8db67e08
--- /dev/null
+++ b/other/pamfix.c
@@ -0,0 +1,291 @@
+/*============================================================================
+                                 pamfix
+==============================================================================
+  Salvage a Netpbm image that is corrupted in certain ways.
+
+  By Bryan Henderson, January 2007.
+
+  Contributed to the public domain by its author.
+============================================================================*/
+
+#include <setjmp.h>
+
+#include "pm_c_util.h"
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;   /* File name of input file */
+    unsigned int verbose;
+    unsigned int truncate;
+    unsigned int changemaxval;
+    unsigned int clip;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENT3(0, "verbose",      OPT_FLAG, NULL, &cmdlineP->verbose,      0);
+    OPTENT3(0, "truncate",     OPT_FLAG, NULL, &cmdlineP->truncate,     0);
+    OPTENT3(0, "changemaxval", OPT_FLAG, NULL, &cmdlineP->changemaxval, 0);
+    OPTENT3(0, "clip",         OPT_FLAG, NULL, &cmdlineP->clip,         0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We don't parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    if (cmdlineP->changemaxval && cmdlineP->clip)
+        pm_error("You cannot specify both -changemaxval and -clip");
+}
+
+
+
+static unsigned int readErrRow;
+static bool readErrVerbose;
+
+static pm_usererrormsgfn handleRowErrMsg;
+
+static void
+handleRowErrMsg(const char * const msg) {
+    if (readErrVerbose)
+        pm_message("Error reading row %u: %s", readErrRow, msg);
+}
+
+
+static sample
+highestSampleInRow(const struct pam * const pamP,
+                   tuple *            const tuplerow) {
+
+    unsigned int col;
+    sample highestSoFar;
+
+    for (col = 0, highestSoFar = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane)
+            highestSoFar = MAX(highestSoFar, tuplerow[col][plane]);
+    }
+    return highestSoFar;
+}
+
+
+
+static void
+analyzeRaster(const struct pam * const pamP,
+              unsigned int *     const goodRowCountP,
+              sample *           const highestSampleP,
+              bool               const mustAbortOnReadError,
+              bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Go through the raster at which the stream described by *tweakedPamP is
+   presently positioned and count how many rows can be successfully read
+   (including validating the samples against pamP->maxval) and determine the
+   highest sample value in those rows.
+
+   Leave the stream positioned arbitrarily.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+    jmp_buf jmpbuf;
+    int rc;
+    
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pm_setusererrormsgfn(handleRowErrMsg);
+
+    rc = setjmp(jmpbuf);
+    if (rc == 0) {
+        pm_setjmpbuf(&jmpbuf);
+
+        readErrVerbose  = mustAbortOnReadError || verbose;
+        *goodRowCountP  = 0;  /* initial value */
+        *highestSampleP = 0;  /* initial value */
+
+        for (row = 0; row < pamP->height; ++row) {
+            readErrRow = row;
+            pnm_readpamrow(pamP, tuplerow);
+            /* The above does not return if it can't read the next row from
+               the file.  Instead, it longjmps out of this loop.
+
+               Update return stats now in case the next iteration longjmps out.
+            */
+            *highestSampleP =
+                MAX(*highestSampleP,
+                    highestSampleInRow(pamP, tuplerow));
+            ++*goodRowCountP;
+        }
+    } else {
+        /* pnm_readpamrow() encountered an error and longjmped */
+        if (mustAbortOnReadError) {
+            /* handleRowErrMsg() has issued the error message */
+            exit(1);
+        }
+    }
+    pm_setjmpbuf(NULL);
+
+    pm_setusererrormsgfn(NULL);
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+clipPamRow(const struct pam * const pamP,
+           tuple *            const tuplerow,
+           unsigned int       const row,
+           bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Clip every sample value in tuplerow[] to the maxval.
+
+   If 'verbose' is true, issue messages about every clipping, indicating it
+   is in row 'row'.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            if (tuplerow[col][plane] > pamP->maxval) {
+                if (verbose) 
+                    pm_message("Clipping: Row %u Col %u Plane %u.  "
+                               "Sample value %lu exceeds the "
+                               "image maxval of %lu",
+                               row, col, plane, tuplerow[col][plane],
+                               pamP->maxval);
+	            tuplerow[col][plane] = pamP->maxval;
+            }
+        }
+    }
+}
+
+
+
+static void
+copyGoodRows(const struct pam * const inpamP,
+             const struct pam * const outpamP,
+             bool               const verbose) {
+/*----------------------------------------------------------------------------
+  Copy the raster of the input stream described by *inpamP to the output
+  stream described by *outpamP.  Copy only as many rows as *outpamP allows;
+  assume *outpamP specifies at most the number of rows of input.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    for (row = 0; row < outpamP->height; ++row) {
+        pnm_readpamrow(inpamP, tuplerow);
+        clipPamRow(outpamP, tuplerow, row, verbose);
+        pnm_writepamrow(outpamP, tuplerow);
+    }
+    
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    struct cmdlineInfo cmdline;
+    struct pam inpam;
+    struct pam outpam;
+    struct pam tweakedPam;
+    FILE * ifP;
+    pm_filepos rasterPos;
+    unsigned int goodRowCount;
+    sample highestSample;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr_seekable(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    /* Tweak maxval to circumvent out-of-bounds sample value check
+       in libpam.  See function validatePamRow() in libpamread.c .
+
+       Ideally we would like to set tweaked-maxval higher for the
+       plain (text) formats.  We make a compromise to keep things
+       simple.
+    */
+
+    tweakedPam = inpam;  /* initial value */
+
+    if ((cmdline.clip || cmdline.changemaxval)
+        && PNM_FORMAT_TYPE(inpam.format) != PBM_TYPE)
+        tweakedPam.maxval =
+            (((sample) 1) << tweakedPam.bytes_per_sample * 8) - 1;
+
+    analyzeRaster(&tweakedPam, &goodRowCount, &highestSample,
+                  !cmdline.truncate, cmdline.verbose);
+
+    if (goodRowCount == 0)
+        pm_error("Cannot read a single row from the image%s",
+                 cmdline.verbose ? "" : ".  Use -verbose to find out why");
+
+    if (goodRowCount < inpam.height)
+        pm_message("Copying %u good rows; %u bottom rows missing%s",
+                   goodRowCount, inpam.height - goodRowCount,
+                   cmdline.verbose ? "" : ".  Use -verbose to find out why");
+
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+
+    outpam = inpam;  /* initial value */
+
+    outpam.file = stdout;
+    outpam.height = goodRowCount;
+    if (cmdline.changemaxval && highestSample > outpam.maxval) {
+        pm_message("Raising maxval from %lu to %lu to legitimize "
+                   "all sample values",
+                    outpam.maxval, highestSample);
+        outpam.maxval = highestSample;
+    }
+
+    pnm_writepaminit(&outpam);
+
+    copyGoodRows(&tweakedPam, &outpam, cmdline.verbose);
+
+    pm_close(inpam.file);
+    
+    return 0;
+}
+
+
+
diff --git a/other/pamfixtrunc b/other/pamfixtrunc
new file mode 100755
index 00000000..1aa3ff94
--- /dev/null
+++ b/other/pamfixtrunc
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
+##############################################################################
+#  This is nothing but a compatibility interface for Pamfixtrunc.
+#  An old program coded to call Pamfixtrunc will continue working because
+#  this interface exists.  All new (or newly modified) programs should
+#  call Pamfix instead.
+##############################################################################
+
+use strict;
+use File::Basename;
+use Cwd 'abs_path';
+
+my @pamFixOptions;
+
+@pamFixOptions = @ARGV;  # initial value
+
+push(@pamFixOptions, '-truncate');
+
+# We want to get Pamfix from the same directory we came from if
+# it's there.  Frequently, the directory containing Netpbm programs is
+# not in the PATH and we were invoked by absolute path.
+
+my $my_directory = abs_path(dirname($0));
+$ENV{"PATH"} = $my_directory . ":" . $ENV{"PATH"};
+
+exit(system('pamfix', @pamFixOptions)>>8);
diff --git a/other/pamfixtrunc.c b/other/pamfixtrunc.c
deleted file mode 100644
index 6d71406f..00000000
--- a/other/pamfixtrunc.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*============================================================================
-                             pamfixtrunc
-==============================================================================
-  Fix a Netpbm image that has been truncated, e.g. by I/O error.
-
-  By Bryan Henderson, January 2007.
-
-  Contributed to the public domain by its author.
-
-============================================================================*/
-
-#include <setjmp.h>
-
-#include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
-#include "mallocvar.h"
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char * inputFilespec;  /* Filespec of input file */
-    unsigned int verbose;
-};
-
-
-
-static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optEntry * option_def;
-
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    MALLOCARRAY(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdlineP->verbose,       0);
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We don't parms that are negative numbers */
-
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-    if (argc-1 == 0) 
-        cmdlineP->inputFilespec = "-";
-    else if (argc-1 != 1)
-        pm_error("Program takes zero or one argument (filename).  You "
-                 "specified %d", argc-1);
-    else
-        cmdlineP->inputFilespec = argv[1];
-}
-
-
-
-static unsigned int readErrRow;
-static bool readErrVerbose;
-
-static pm_usererrormsgfn discardMsg;
-
-static void
-discardMsg(const char * const msg) {
-    if (readErrVerbose)
-        pm_message("Error reading row %u: %s", readErrRow, msg);
-}
-
-
-
-static void
-countRows(const struct pam * const inpamP,
-          bool               const verbose,
-          unsigned int *     const goodRowCountP) {
-
-    tuple * tuplerow;
-    unsigned int row;
-    jmp_buf jmpbuf;
-    int rc;
-    unsigned int goodRowCount;
-    
-    tuplerow = pnm_allocpamrow(inpamP);
-
-    pm_setusererrormsgfn(discardMsg);
-
-    rc = setjmp(jmpbuf);
-    if (rc == 0) {
-        pm_setjmpbuf(&jmpbuf);
-
-        readErrVerbose = verbose;
-        goodRowCount = 0;  /* initial value */
-        for (row = 0; row < inpamP->height; ++row) {
-            readErrRow = row;
-            pnm_readpamrow(inpamP, tuplerow);
-            /* The above does not return if it can't read the next row from
-               the file.  Instead, it longjmps out of this loop.
-            */
-            ++goodRowCount;
-        }
-    }
-    *goodRowCountP = goodRowCount;
-
-    pnm_freepamrow(tuplerow);
-}
-
-
-
-static void
-copyGoodRows(const struct pam * const inpamP,
-             FILE *             const ofP,
-             unsigned int       const goodRowCount) {
-
-    struct pam outpam;
-    tuple * tuplerow;
-    unsigned int row;
-
-    outpam = *inpamP;  /* initial value */
-
-    outpam.file = ofP;
-    outpam.height = goodRowCount;
-
-    tuplerow = pnm_allocpamrow(inpamP);
-
-    pnm_writepaminit(&outpam);
-
-    for (row = 0; row < outpam.height; ++row) {
-        pnm_readpamrow(inpamP, tuplerow);
-        pnm_writepamrow(&outpam, tuplerow);
-    }
-    
-    pnm_freepamrow(tuplerow);
-}
-
-
-
-int
-main(int argc, char * argv[]) {
-    struct cmdlineInfo cmdline;
-    struct pam inpam;
-    FILE * ifP;
-    pm_filepos rasterPos;
-    unsigned int goodRowCount;
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr_seekable(cmdline.inputFilespec);
-
-    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
-
-    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
-
-    countRows(&inpam, cmdline.verbose, &goodRowCount);
-
-    pm_message("Copying %u good rows; %u bottom rows missing",
-               goodRowCount, inpam.height - goodRowCount);
-    
-    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
-
-    copyGoodRows(&inpam, stdout, goodRowCount);
-
-    pm_close(inpam.file);
-    
-    return 0;
-}
-
-
diff --git a/other/pamlookup.c b/other/pamlookup.c
index 3b7a7f59..4ceb047f 100644
--- a/other/pamlookup.c
+++ b/other/pamlookup.c
@@ -13,32 +13,36 @@
 
 ============================================================================*/
 
+#include <assert.h>
+
 #include "pm_c_util.h"
-#include "pam.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "pm_system.h"
 #include "nstring.h"
+#include "pam.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *indexFilespec;  
-    char *lookupFilespec;
-    char *missingcolor;  /* -missingcolor value.  null if not specified */
-    unsigned int fit;  /* -fit option */
+    const char * indexFilespec;  
+    char *       lookupFilespec;
+    char *       missingcolor;  /* null if not specified */
+    unsigned int fit;
+    unsigned int byplane;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
     optStruct3 opt;
@@ -47,6 +51,8 @@ parseCommandLine(int argc, char ** const argv,
     
     unsigned int lookupfileSpec, missingcolorSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "lookupfile",     OPT_STRING, &cmdlineP->lookupFilespec,  
             &lookupfileSpec, 0);
@@ -54,12 +60,14 @@ parseCommandLine(int argc, char ** const argv,
             &cmdlineP->missingcolor,   &missingcolorSpec, 0);
     OPTENT3(0,   "fit", OPT_FLAG, 
             NULL,   &cmdlineP->fit, 0);
+    OPTENT3(0,   "byplane", OPT_FLAG, 
+            NULL,   &cmdlineP->byplane, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!lookupfileSpec)
@@ -68,10 +76,15 @@ parseCommandLine(int argc, char ** const argv,
     if (!missingcolorSpec)
         cmdlineP->missingcolor = NULL;
 
+    if (cmdlineP->byplane && cmdlineP->missingcolor)
+        pm_error("You cannot specify -missingcolor with -byplane");
+
     if (argc-1 < 1)
         cmdlineP->indexFilespec = "-";
     else
         cmdlineP->indexFilespec = argv[1];
+
+    free(option_def);
 }        
 
 
@@ -94,7 +107,7 @@ fitLookup(tuple **     const inputLookup,
     fitLookuppamP->width = cols;
     fitLookuppamP->height = rows;
 
-    asprintfN(&pamscaleCommand, "pamscale -width=%u -height=%u", cols, rows);
+    pm_asprintf(&pamscaleCommand, "pamscale -width=%u -height=%u", cols, rows);
 
     inPamtuples.pamP = (struct pam *) &inputLookuppam;
     inPamtuples.tuplesP = (tuple ***) &inputLookup;
@@ -105,25 +118,38 @@ fitLookup(tuple **     const inputLookup,
               &pm_accept_to_pamtuples, &outPamtuples,
               pamscaleCommand);
 
-    strfree(pamscaleCommand);
+    pm_strfree(pamscaleCommand);
 }
 
 
 
 static void
-getLookup(const char * const lookupFilespec, 
+getLookup(const char * const lookupFileName, 
           unsigned int const indexDegree,
           unsigned int const indexMaxval,
           tuple ***    const lookupP,
           struct pam * const lookuppamP,
           bool         const fit) {
+/*----------------------------------------------------------------------------
+   Get the lookup image (the one that maps integers to tuples, e.g. a
+   color index / color map / palette) from the file named 
+   'lookupFileName'.
 
-    FILE*  lookupfileP;
+   Interpret the lookup image for use with indices that are ntuples of size
+   'indexDegree' (normally 1, could be 2) whose elements range from 0 through
+   'indexMaxval'
+
+   Iff 'fit' is true, stretch or compress the image in the file to fit
+   exactly the range identified by 'indexMaxval'.
+
+   Return the image as *lookupP and *lookuppamP.
+-----------------------------------------------------------------------------*/
+    FILE *  lookupfileP;
 
     struct pam inputLookuppam;
-    tuple** inputLookup;
+    tuple ** inputLookup;
 
-    lookupfileP = pm_openr(lookupFilespec);
+    lookupfileP = pm_openr(lookupFileName);
     inputLookup = pnm_readpam(lookupfileP, 
                               &inputLookuppam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -153,14 +179,14 @@ getLookup(const char * const lookupFilespec,
     if (indexDegree == 2 && lookuppamP->height - 1 > indexMaxval)
         pm_message("Warning: your lookup table image is taller than "
                    "the maxval of "
-                   "your index message, so the bottom end of the lookup "
+                   "your index image, so the bottom end of the lookup "
                    "table image has no effect on the output.");
 }
 
 
 
 static void
-computeDefaultTuple(struct cmdlineInfo const cmdline, 
+computeDefaultTuple(struct CmdlineInfo const cmdline, 
                     tuple **           const lookup,
                     struct pam *       const lookuppamP, 
                     tuple *            const defaultTupleP) {
@@ -203,20 +229,94 @@ computeDefaultTuple(struct cmdlineInfo const cmdline,
 
 
 static void
-doLookup(struct pam const indexpam,
-         struct pam const outpamarg,
-         tuple      const defaultTuple,
-         tuple **   const lookup,
-         struct pam const lookuppam) {
+doLookupByPlane(struct pam const indexpam,
+                tuple **   const lookup,
+                struct pam const lookuppam,
+                FILE *     const ofP) {
+/*----------------------------------------------------------------------------
+   Write an output image to *ofP derived from the input image read per
+   'indexpam' (now positioned to its raster).
 
+   Base each tuple of the output on the tuple in the same place in the input.
+   Look up each sample of the input tuple in the lookupt image given by
+   'lookup' and 'lookuppam' to get the corresponding sample of the output
+   image.
+
+   Our output image has the same width, height, depth, image type, and tuple
+   type as the input image and the same maxval as the lookup image.
+
+   We ignore any plane or row after the first in the lookup image.  We expect
+   its width to match the maxval of the input image.
+-----------------------------------------------------------------------------*/
     struct pam outpam;
     unsigned int row;
 
     tuple* tuplerowIndex;
     tuple* tuplerowOut;
 
-    outpam = outpamarg;
+    outpam = indexpam;  /* initial value */
+    outpam.maxval = lookuppam.maxval;
+    outpam.file = ofP;
+    
+    tuplerowIndex = pnm_allocpamrow(&indexpam);
+    tuplerowOut = pnm_allocpamrow(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    assert(lookuppam.width == indexpam.maxval + 1);
+        /* Calling condition */
+
+    for (row = 0; row < indexpam.height; ++row) {
+        unsigned int col;
+        pnm_readpamrow(&indexpam, tuplerowIndex);
+        
+        for (col = 0; col < indexpam.width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < indexpam.depth; ++plane) {
+                unsigned int const index = tuplerowIndex[col][plane];
+
+                if (index > lookuppam.maxval)
+                    pm_error("Sample value %u in the lookup image exceeds "
+                             "the lookup image's maxval (%u)",
+                             index, (unsigned)lookuppam.maxval);
+
+                tuplerowOut[col][plane] = lookup[0][index][0];
+            }
+        }
+        pnm_writepamrow(&outpam, tuplerowOut);
+    }
+    pnm_freepamrow(tuplerowIndex);
+    pnm_freepamrow(tuplerowOut);
+}
+
+
+
+static void
+doLookupWholeTuple(struct pam const indexpam,
+                   tuple      const defaultTuple,
+                   tuple **   const lookup,
+                   struct pam const lookuppam,
+                   FILE *     const ofP) {
+/*----------------------------------------------------------------------------
+   Write an output image to *ofP derived from the input image read per
+   'indexpam' (now positioned to its raster).
+
+   For each tuple of the output, use the corresponding tuple of the input as
+   an index into the lookup image given by 'lookup' and 'lookuppam'.  If that
+   index is not present in the lookup image, put 'defaultTuple' in the output.
+-----------------------------------------------------------------------------*/
+    struct pam outpam;
+    unsigned int row;
+
+    tuple* tuplerowIndex;
+    tuple* tuplerowOut;
 
+    outpam = lookuppam;  /* initial value */
+    outpam.height = indexpam.height;
+    outpam.width = indexpam.width;
+    outpam.file = ofP;
+    
     tuplerowIndex = pnm_allocpamrow(&indexpam);
     tuplerowOut = pnm_allocpamrow(&outpam);
 
@@ -254,18 +354,18 @@ doLookup(struct pam const indexpam,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** const argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam indexpam;
-    struct pam outpam;
-    FILE*  ifP;
+    FILE * ifP;
+    unsigned int indexDegree;
     struct pam lookuppam;
-    tuple** lookup;
+    tuple ** lookup;
 
     tuple defaultTuple;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -273,28 +373,30 @@ main(int argc, char *argv[]) {
 
     pnm_readpaminit(ifP, &indexpam, PAM_STRUCT_SIZE(tuple_type));
 
-    if (indexpam.depth != 1 && indexpam.depth != 2)
-        pm_error("The input (index) file must have depth 1 or 2.  "
-                 "Yours has depth %d",
+    if (!cmdline.byplane && (indexpam.depth != 1 && indexpam.depth != 2))
+        pm_error("Unless you specify -byplane, "
+                 "the input (index) file must have depth 1 or 2.  "
+                 "Yours has depth %u",
                  indexpam.depth);
 
-    getLookup(cmdline.lookupFilespec, indexpam.depth, indexpam.maxval, 
-              &lookup, &lookuppam, cmdline.fit);
+    indexDegree = cmdline.byplane ? 1 : indexpam.depth;
+
+    getLookup(cmdline.lookupFilespec, indexDegree, indexpam.maxval, 
+              &lookup, &lookuppam, cmdline.fit || cmdline.byplane);
 
     computeDefaultTuple(cmdline, lookup, &lookuppam, &defaultTuple);
 
-    outpam = lookuppam;
-    outpam.height = indexpam.height;
-    outpam.width = indexpam.width;
-    outpam.file = stdout;
-    
-    doLookup(indexpam, outpam, defaultTuple, lookup, lookuppam);
+    if (cmdline.byplane)
+        doLookupByPlane(indexpam, lookup, lookuppam, stdout);
+    else
+        doLookupWholeTuple(indexpam, defaultTuple, lookup, lookuppam, stdout);
 
     pm_close(ifP);
 
     pnm_freepamtuple(defaultTuple);
     pnm_freepamarray(lookup, &lookuppam);
     
-    exit(0);
+    return 0;
 }
 
+
diff --git a/other/pampick.c b/other/pampick.c
index 63f32968..61941f06 100644
--- a/other/pampick.c
+++ b/other/pampick.c
@@ -92,7 +92,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the pointers we place into *cmdlineP are sometimes to storage
@@ -119,7 +119,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     initUintSet(&cmdlineP->imageSeqList, argc-1);
@@ -217,15 +217,15 @@ failIfUnpickedImages(const struct uintSet * const uintSetP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
 
-    bool eof;  /* No more images in input */
+    int eof;  /* No more images in input */
     unsigned int imageSeq;  
         /* Sequence of current image in input file.  First = 0 */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
diff --git a/other/pamsplit.c b/other/pamsplit.c
index a03353d7..26eb0d59 100644
--- a/other/pamsplit.c
+++ b/other/pamsplit.c
@@ -16,10 +16,10 @@
 #include <stdio.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
 #include "mallocvar.h"
+#include "pam.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -34,7 +34,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the pointers we place into *cmdlineP are sometimes to storage
@@ -59,7 +59,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!padnameSpec)
@@ -138,11 +138,11 @@ computeOutputName(char          const outputFilePattern[],
     afterSub = strstr(outputFilePattern, "%d") + 2;
 
     /* Make filenameFormat something like "%s%04u%s" */
-    asprintfN(&filenameFormat, "%%s%%0%ud%%s", padCount);
+    pm_asprintf(&filenameFormat, "%%s%%0%ud%%s", padCount);
 
-    asprintfN(outputNameP, filenameFormat, beforeSub, imageSeq, afterSub);
+    pm_asprintf(outputNameP, filenameFormat, beforeSub, imageSeq, afterSub);
 
-    strfree(filenameFormat);
+    pm_strfree(filenameFormat);
 
     free(beforeSub);
 }
@@ -150,16 +150,16 @@ computeOutputName(char          const outputFilePattern[],
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
 
     FILE * ifP;
-    bool eof;  /* No more images in input */
+    int eof;  /* No more images in input */
     unsigned int imageSeq;  
         /* Sequence of current image in input file.  First = 0 */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
@@ -179,7 +179,7 @@ main(int argc, char *argv[]) {
         extractOneImage(ifP, ofP);
 
         pm_close(ofP);
-        strfree(outputFileName);
+        pm_strfree(outputFileName);
 
         pnm_nextimage(ifP, &eof);
     }
@@ -187,3 +187,6 @@ main(int argc, char *argv[]) {
     
     return 0;
 }
+
+
+
diff --git a/other/pamstack.c b/other/pamstack.c
index d826cf1f..308852c8 100644
--- a/other/pamstack.c
+++ b/other/pamstack.c
@@ -44,7 +44,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
     extern struct pam pam;  /* Just so we can look at field sizes */
@@ -62,7 +62,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -70,7 +70,8 @@ parseCommandLine(int argc, char ** argv,
     else
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("Tuple type name specified is too long.  Maximum of "
-                     "%u characters allowed.", sizeof(pam.tuple_type));
+                     "%u characters allowed.",
+                     (unsigned)sizeof(pam.tuple_type));
 
     cmdlineP->nInput = 0;  /* initial value */
     { 
@@ -215,10 +216,10 @@ nextImageAllStreams(unsigned int const nInput,
     unsigned int inputSeq;
 
     for (inputSeq = 0; inputSeq < nInput; ++inputSeq) {
-        bool eof;
+        int eof;
         pnm_nextimage(ifP[inputSeq], &eof);
         if (eof)
-            *eofP = eof;
+            *eofP = true;
     }
 }
 
diff --git a/other/pamsummcol.c b/other/pamsummcol.c
index c31a9940..c84f38ad 100644
--- a/other/pamsummcol.c
+++ b/other/pamsummcol.c
@@ -54,7 +54,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (sumSpec + minSpec + maxSpec > 1)
diff --git a/other/pamunlookup.c b/other/pamunlookup.c
new file mode 100644
index 00000000..77c2807b
--- /dev/null
+++ b/other/pamunlookup.c
@@ -0,0 +1,250 @@
+/*============================================================================
+                               pamunlookup
+==============================================================================
+  Find tuple values from an input image in a lookup table and
+  produce a corresponding index image containing table indices.
+
+  The lookup table is a one-row PAM image tuple type the same as the input
+  image.  The output index image has the same width and height as the input
+  image depth 1, and maxval equal to the width of the lookup image (the
+  possible values include one for each column in the lookup image, plus one
+  for tuple values that are not in the lookup image).
+
+  By Bryan Henderson, San Jose CA 2015.08.08
+
+============================================================================*/
+
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm_system.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    char *       lookupfile;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    
+    unsigned int lookupfileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "lookupfile",     OPT_STRING, &cmdlineP->lookupfile,  
+            &lookupfileSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!lookupfileSpec)
+        pm_error("You must specify the -lookupfile option");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}        
+
+
+
+static void
+getLookup(const char * const lookupFileName, 
+          tuple ***    const lookupP,
+          struct pam * const lookuppamP) {
+/*----------------------------------------------------------------------------
+   Get the lookup image (the one that maps integers to tuples, e.g. a
+   color index / color map / palette) from the file named 
+   'lookupFileName'.
+
+   Return the image as *lookupP and *lookuppamP.
+-----------------------------------------------------------------------------*/
+    FILE *  lookupfileP;
+
+    struct pam inputLookuppam;
+    tuple ** inputLookup;
+
+    lookupfileP = pm_openr(lookupFileName);
+    inputLookup = pnm_readpam(lookupfileP, 
+                              &inputLookuppam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_close(lookupfileP);
+    
+    if (inputLookuppam.height != 1)
+        pm_error("The lookup table image must be one row.  "
+                 "Yours is %u rows.", 
+                 inputLookuppam.height);
+
+    *lookupP = inputLookup;
+    *lookuppamP = inputLookuppam;
+}
+
+
+
+static void
+makeReverseLookupHash(struct pam * const lookuppamP,
+                      tuple **     const lookup,
+                      tuplehash *  const hashP) {
+/*----------------------------------------------------------------------------
+   Create a tuple hash that maps each tuple values in the first row of
+   'lookup' to the number of the column in which it appears.
+
+   Abort the program with an error if the same tuple value occurs in two
+   columns of the first row.
+-----------------------------------------------------------------------------*/
+    tuplehash hash;
+    unsigned int col;
+
+    hash = pnm_createtuplehash();
+
+    for (col = 0; col < lookuppamP->width; ++col) {
+        tuple const thisValue = lookup[0][col];
+        
+        int found;
+        int priorValue;
+
+        pnm_lookuptuple(lookuppamP, hash, thisValue, &found, &priorValue);
+
+        if (found)
+            pm_error("Same tuple value occurs in both Column %u and "
+                     "Column %u of the lookup image", priorValue, col);
+        else {
+            int fits;
+            pnm_addtotuplehash(lookuppamP, hash, lookup[0][col], col, &fits);
+
+            if (!fits)
+                pm_error("Out of memory constructing hash of lookup table");
+        }
+    }
+
+    *hashP = hash;
+}
+
+
+
+static void
+doUnlookup(struct pam * const inpamP,
+           tuplehash    const lookupHash,
+           sample       const maxIndex,
+           FILE *       const ofP) {
+
+    struct pam outpam;
+    unsigned int row;
+    tuple * inrow;
+    tuple * outrow;
+
+    inrow = pnm_allocpamrow(inpamP);
+
+    outpam.size = sizeof(outpam);
+    outpam.len = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file = ofP;
+    outpam.format = PAM_FORMAT;
+    outpam.height = inpamP->height;
+    outpam.width = inpamP->width;
+    outpam.depth = 1;
+    outpam.maxval = maxIndex + 1;  /* +1 for missing color */
+    strcpy(outpam.tuple_type, "INDEX");
+
+    pnm_writepaminit(&outpam);
+
+    outrow = pnm_allocpamrow(&outpam);
+
+    for (row = 0; row < inpamP->height; ++row) {
+        unsigned int col;
+        
+        pnm_readpamrow(inpamP, inrow);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            int found;
+            int index;
+            pnm_lookuptuple(inpamP, lookupHash, inrow[col], &found, &index);
+
+            if (found) {
+                assert(index <= outpam.maxval);
+                outrow[col][0] = index;
+            } else
+                outrow[col][0] = maxIndex + 1;
+        }
+        pnm_writepamrow(&outpam, outrow);
+    }
+
+    pnm_freepamrow(outrow);
+    pnm_freepamrow(inrow);
+}
+
+
+
+int
+main(int argc, const char ** const argv) {
+
+    struct CmdlineInfo cmdline;
+    struct pam inpam;
+    FILE * ifP;
+    struct pam lookuppam;
+    tuple ** lookup;
+
+    tuplehash lookupHash;
+    
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    getLookup(cmdline.lookupfile, &lookup, &lookuppam);
+
+    if (inpam.depth != lookuppam.depth)
+        pm_error("The lookup image has depth %u, but the input image "
+                 "has depth %u.  They must be the same",
+                 lookuppam.depth, inpam.depth);
+    if (!streq(inpam.tuple_type, lookuppam.tuple_type))
+        pm_error("The lookup image has tupel type '%s', "
+                 "but the input image "
+                 "has tuple type '%s'.  They must be the same",
+                 lookuppam.tuple_type, inpam.tuple_type);
+
+    makeReverseLookupHash(&lookuppam, lookup, &lookupHash);
+
+    doUnlookup(&inpam, lookupHash, lookuppam.width-1, stdout);
+
+    pm_close(ifP);
+
+    pnm_destroytuplehash(lookupHash);
+    pnm_freepamarray(lookup, &lookuppam);
+    
+    return 0;
+}
+
+
diff --git a/other/pamvalidate.c b/other/pamvalidate.c
new file mode 100644
index 00000000..a7b08b8e
--- /dev/null
+++ b/other/pamvalidate.c
@@ -0,0 +1,86 @@
+/*=============================================================================
+                               pamvalidate
+===============================================================================
+  Part of the Netpbm package.
+
+  Copy PAM and PNM (i.e. PBM, PGM, or PPM) images from Standard Input
+  to Standard Output.  No output when input is invalid.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+
+#include <string.h>
+#include "pm_c_util.h"
+#include "pam.h"
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * const tmpfile = pm_tmpfile();
+    int        eof;     /* no more images in input stream */
+    struct pam inpam;   /* Input PAM image */
+    struct pam outpam;  /* Output PAM image */
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 != 0)
+        pm_error("Program takes no arguments.  Input is from Standard Input");
+
+    for (eof = FALSE; !eof; ) {
+        pnm_readpaminit(stdin, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+        outpam = inpam;  /* initial value */
+        outpam.file = tmpfile;
+
+        pnm_writepaminit(&outpam);
+
+        if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE) {
+            /* Fast method for PBM */
+            unsigned char * const inrow = pbm_allocrow_packed(inpam.width);
+
+            unsigned int row;
+
+            for (row = 0; row < inpam.height; ++row) {
+                pbm_readpbmrow_packed(inpam.file, inrow, inpam.width,
+                                      inpam.format);
+                pbm_writepbmrow_packed(tmpfile, inrow, inpam.width, 0);
+            }
+            pbm_freerow(inrow);
+
+        } else {
+            /* General case.  Logic works for PBM */
+            tuple * const tuplerow = pnm_allocpamrow(&inpam);
+
+            unsigned int row;
+
+            for (row = 0; row < inpam.height; ++row) {
+                pnm_readpamrow(&inpam, tuplerow);
+                pnm_writepamrow(&outpam, tuplerow);
+            }
+            pnm_freepamrow(tuplerow);
+        }
+        pnm_nextimage(stdin, &eof);
+    }
+
+    fseek(tmpfile, 0, SEEK_SET);
+
+    while (!feof(tmpfile) && !ferror(tmpfile) && !ferror(stdout)) {
+        char buffer[4096];
+        size_t bytesReadCt;
+
+        bytesReadCt = fread(buffer, 1, sizeof(buffer), tmpfile);
+
+        if (ferror(tmpfile))
+            pm_error("Error reading from temporary file.  "
+                     "Incomplete output.  "    
+                     "Errno = %s (%d)", strerror(errno), errno);
+        else
+            fwrite(buffer, 1, bytesReadCt, stdout);
+    }
+    pm_close(tmpfile);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/other/pamx/Makefile b/other/pamx/Makefile
index a40ea3a6..4e06e0fd 100644
--- a/other/pamx/Makefile
+++ b/other/pamx/Makefile
@@ -8,30 +8,37 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 include $(BUILDDIR)/config.mk
 
 EXTERN_INCLUDE =
-ifneq ($(X11LIB),NONE)
-  ifneq ($(X11HDR_DIR),)
-    EXTERN_INCLUDES += -I$(X11HDR_DIR)
+
+ifeq ($(shell pkg-config x11 --modversion --silence-errors),)
+  # Pkg-config has never heard of X11, or doesn't even exist
+
+  ifneq ($(X11LIB),NONE)
+    HAVE_X11LIB = Y
+    ifneq ($(X11HDR_DIR)x,x)
+      EXTERN_INCLUDES += -I$(X11HDR_DIR)
+    endif
   endif
+else
+  HAVE_X11LIB = Y
+  X11LIB = $(shell pkg-config x11 --libs)
+  EXTERN_INCLUDES += $(shell pkg-config x11 --cflags)
 endif
 
-ifneq ($(X11LIB),NONE)
-  BINARIES += pamx
-
-  PAMX_OBJECTS = \
-	  pamx.o \
-	  image.o \
-	  send.o \
-	  window.o \
+ifeq ($(HAVE_X11LIB),Y)
+  PORTBINARIES += pamx
 
-  MERGE_OBJECTS = \
-	  pamx.o2 \
+  EXTRA_OBJECTS = \
 	  image.o \
 	  send.o \
 	  window.o \
 
 endif
 
-OBJECTS = $(PAMX_OBJECTS)
+BINARIES = $(PORTBINARIES)
+
+OBJECTS = $(BINARIES:%=%.o) $(EXTRA_OBJECTS)
+
+MERGE_OBJECTS = $(BINARIES:%=%.o2) $(EXTRA_OBJECTS)
 
 MERGEBINARIES = $(BINARIES)
 
@@ -39,7 +46,12 @@ all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-pamx: $(PAMX_OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $(PAMX_OBJECTS) \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(X11LIB)) \
-	  $(LDFLAGS) $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD)
+ifeq ($(shell pkg-config x11 --libs),)
+  X11_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(X11LIB))
+else
+  X11_LIBOPTS = $(shell pkg-config x11 --libs)
+endif
+
+pamx: image.o send.o window.o
+pamx: ADDL_OBJECTS = image.o send.o window.o
+pamx: LDFLAGS_TARGET = $(X11_LIBOPTS)
diff --git a/other/pamx/pamx.c b/other/pamx/pamx.c
index 130cc64c..e22693ea 100644
--- a/other/pamx/pamx.c
+++ b/other/pamx/pamx.c
@@ -3,6 +3,7 @@
    Copyright information is in the file COPYRIGHT
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <signal.h>
 #include <unistd.h>
@@ -65,7 +66,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options. */
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
   
     unsigned int option_def_index;
@@ -110,7 +111,7 @@ parseCommandLine(int argc,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
     
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (geometrySpec) {
@@ -360,7 +361,7 @@ main(int     argc,
     destroyViewer(viewerP);
 
     if (title)
-        strfree(title);
+        pm_strfree(title);
 
     XCloseDisplay(dispP);
 
diff --git a/other/pamx/send.c b/other/pamx/send.c
index 5413308b..fd50d5e9 100644
--- a/other/pamx/send.c
+++ b/other/pamx/send.c
@@ -78,8 +78,8 @@ ximageToPixmap(Display *    const disp,
 
 
 
-/* find the best pixmap depth supported by the server for a particular
- * visual and return that depth.
+/* find the best pixmap depth the server can do for a particular visual and
+ * return that depth.
  *
  * this is complicated by R3's lack of XListPixmapFormats so we fake it
  * by looking at the structure ourselves.
@@ -587,7 +587,7 @@ makeXImage(XImageInfo * const ximageinfoP,
         MALLOCARRAY(data, byteCount);
         if (data == NULL)
             pm_error("Can't allocate space for %u byte image", byteCount);
-        bcopy(imageP->data, data, byteCount);
+        memcpy(data, imageP->data, byteCount);
 
         ximageinfoP->ximageP =
             XCreateImage(disp, visualP, 1, XYBitmap,
diff --git a/other/pamx/window.c b/other/pamx/window.c
index 2eb48241..e2de1577 100644
--- a/other/pamx/window.c
+++ b/other/pamx/window.c
@@ -6,6 +6,7 @@
    See COPYRIGHT file for copyright information.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 
 #include <assert.h>
@@ -112,11 +113,11 @@ getInitialViewerGeometry(const char *   const geometryString,
         *userChoseP = TRUE;
     } else if (geometryString) {
         const char * defGeom;
-        asprintfN(&defGeom, "%ux%u+0+0", defaultWidth, defaultHeight);
+        pm_asprintf(&defGeom, "%ux%u+0+0", defaultWidth, defaultHeight);
         XGeometry(dispP, scrn, geometryString, defGeom, 0, 1, 1, 0, 0,
                   (int *)xposP, (int *)yposP,
                   (int *)widthP, (int *)heightP);
-        strfree(defGeom);
+        pm_strfree(defGeom);
         *userChoseP = TRUE;
     } else {
         *widthP     = defaultWidth;
@@ -193,7 +194,7 @@ determineRepaintStrategy(viewer  *    const viewerP,
                         
     /* Decide how we're going to handle repaints.  We have three modes:
        use backing-store, use background pixmap, and use exposures.
-       If the server supports backing-store, we enable it and use it.
+       If the server allows backing-store, we enable it and use it.
        This really helps servers which are memory constrained.  If the
        server does not have backing-store, we try to send the image to
        a pixmap and use that as backing-store.  If that fails, we use
@@ -499,10 +500,10 @@ iconName(const char * const s) {
         char * t;
 
         STRSCPY(buf, s);
-        /* strip off stuff following 1st word.  This strips
+        /* chop off stuff following 1st word.  This strips
            info added by processing functions too.
         */
-        t = index(buf, ' ');
+        t = strchr(buf, ' ');
         if (t)
             *t = '\0';
     
@@ -510,15 +511,15 @@ iconName(const char * const s) {
            You might want to change this.
         */
     
-        t= rindex(buf, '/');
+        t= strrchr(buf, '/');
         if (t) {
             char * p;
             for (p = buf, ++t; *t; ++p, ++t)
                 *p = *t;
             *p = '\0';
         }
-        /* look for an extension and strip it off */
-        t = index(buf, '.');
+        /* Chop off any filename extension */
+        t = strchr(buf, '.');
         if (t)
             *t = '\0';
     }
@@ -652,9 +653,9 @@ bestVisual(Display *      const disp,
     Visual * visualP;
     Visual * default_visualP;
 
-    /* Figure out the best depth the server supports.  note that some servers
-       (such as the HP 11.3 server) actually say they support some depths but
-       have no visuals that support that depth.  Seems silly to me ...
+    /* Figure out the best depth the server allows.  note that some servers
+       (such as the HP 11.3 server) actually say they allow some depths but
+       have no visuals that allow that depth.  Seems silly to me ...
     */
 
     depth = 0;
@@ -1079,7 +1080,7 @@ retvalueFromExitReason(exitReason const exitReason) {
     switch (exitReason) {
     case EXIT_NONE:      assert(false); break;
     case EXIT_QUIT:      retval =  0;     break;
-    case EXIT_WM_KILL:   retval = 10;     break;
+    case EXIT_WM_KILL:   retval =  0;     break;
     case EXIT_DESTROYED: retval = 20;     break;
     }
     return retval;
@@ -1175,7 +1176,7 @@ displayInViewer(viewer *       const viewerP,
     {
         const char * const name = iconName(title);
         XSetIconName(viewerP->dispP, viewerP->viewportWin, name);
-        strfree(name);
+        pm_strfree(name);
     }
     setNormalSizeHints(viewerP, imageP);
 
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index 42b03063..57db4329 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -77,7 +77,7 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -110,7 +110,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
 
@@ -655,7 +655,7 @@ addImageColorsToHash(struct pam *   const pamP,
         pnm_readpamrow(pamP, tuplerow);
 
         for (col = 0; col < pamP->width; ++col) {
-            bool firstOccurrence;
+            int firstOccurrence;
 
             pnm_addtuplefreqoccurrence(pamP, tuplerow[col], tuplehash,
                                        &firstOccurrence);
@@ -688,7 +688,7 @@ computeHistogram(FILE *         const ifP,
     struct pam firstPam;
     tuplehash tuplehash;
     unsigned int colorCount;
-    bool eof;
+    int eof;
     
     pm_message("making histogram...");
 
diff --git a/other/ppmdcfont.c b/other/ppmdcfont.c
index 701277a9..130b6383 100644
--- a/other/ppmdcfont.c
+++ b/other/ppmdcfont.c
@@ -73,14 +73,14 @@ generateCommandTables(const struct ppmd_font * const fontP,
         if (fontP->glyphTable[relativeCodePoint].header.commandCount > 0) {
             const char * commandTableVariableName;
 
-            asprintfN(&commandTableVariableName, "%s_cmd_%u",
-                      glyphTableVariableName,
-                      fontP->header.firstCodePoint + relativeCodePoint);
+            pm_asprintf(&commandTableVariableName, "%s_cmd_%u",
+                        glyphTableVariableName,
+                        fontP->header.firstCodePoint + relativeCodePoint);
             
             generateCommandTable(fontP->glyphTable[relativeCodePoint],
                                  commandTableVariableName);
 
-            strfree(commandTableVariableName);
+            pm_strfree(commandTableVariableName);
 
             fprintf(stdout, "};\n");
             fprintf(stdout, "\n");
@@ -130,14 +130,14 @@ generateGlyphTable(const struct ppmd_font * const fontP,
 
         const char * commandTableVariableName;
 
-        asprintfN(&commandTableVariableName, "%s_cmd_%u",
-                  variableName,
-                  fontP->header.firstCodePoint + relativeCodePoint);
+        pm_asprintf(&commandTableVariableName, "%s_cmd_%u",
+                    variableName,
+                    fontP->header.firstCodePoint + relativeCodePoint);
         
         generateGlyph(fontP->glyphTable[relativeCodePoint],
                       commandTableVariableName);
 
-        strfree(commandTableVariableName);
+        pm_strfree(commandTableVariableName);
 
         if (relativeCodePoint < fontP->header.characterCount - 1)
             fprintf(stdout, "  ,\n");
@@ -184,7 +184,7 @@ main(int argc, char **argv) {
 
     fprintf(stdout, "#include \"ppmdfont.h\"\n\n");
 
-    asprintfN(&glyphTableVariableName, "%s_glyphTable", fontVariableName);
+    pm_asprintf(&glyphTableVariableName, "%s_glyphTable", fontVariableName);
 
     generateGlyphTable(fontP, glyphTableVariableName);
 
@@ -192,7 +192,7 @@ main(int argc, char **argv) {
         
     generateFont(fontP, fontVariableName, glyphTableVariableName);
 
-    strfree(glyphTableVariableName);
+    pm_strfree(glyphTableVariableName);
 
     ppmd_free_font(fontP);
     
diff --git a/other/ppmsvgalib.c b/other/ppmsvgalib.c
index 5bcabdc1..c5700992 100644
--- a/other/ppmsvgalib.c
+++ b/other/ppmsvgalib.c
@@ -46,7 +46,7 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -64,7 +64,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!modeSpec)
@@ -253,10 +253,6 @@ main(int argc, char *argv[]) {
 
     ppm_init(&argc, argv);
 
-    rc = vga_init();         /* Initialize. */
-    if (rc < 0)
-        pm_error("Svgalib unable to allocate a virtual console.");
-
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilespec);
@@ -269,6 +265,14 @@ main(int argc, char *argv[]) {
                   &checkResult);
     }
 
+    /* We wait to initialize Svgalib to prevent it from interfering with
+       error messages the code above might issue, and leaving the console
+       in an undesirable state if the above code aborts the program.
+    */
+    rc = vga_init();         /* Initialize. */
+    if (rc < 0)
+        pm_error("Svgalib unable to allocate a virtual console.");
+
     if (vga_hasmode(cmdline.mode))
         display(ifP, cols, rows, maxval, format, 
                 cmdline.mode, cmdline.verbose);