about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile13
-rw-r--r--analyzer/pgmhist.c327
-rw-r--r--analyzer/ppmhist.c315
-rw-r--r--buildtools/README.pkg70
-rwxr-xr-xbuildtools/configure.pl58
-rw-r--r--buildtools/debian/README122
-rwxr-xr-xbuildtools/debian/mkdeb541
-rwxr-xr-xbuildtools/debian/postinst5
-rwxr-xr-xbuildtools/debian/postrm5
-rw-r--r--common.mk27
-rw-r--r--converter/other/cameratopam/camera.c15
-rw-r--r--converter/other/cameratopam/foveon.c20
-rw-r--r--converter/other/pnmtopng.README101
-rw-r--r--converter/other/pstopnm.c440
-rw-r--r--converter/ppm/ppmtompeg/Makefile2
-rw-r--r--doc/HISTORY75
-rw-r--r--doc/INSTALL14
-rwxr-xr-xeditor/pnmmargin54
-rw-r--r--editor/specialty/ppmrelief.c112
-rw-r--r--generator/pgmkernel.c26
-rw-r--r--generator/pgmmake.c18
-rw-r--r--generator/pgmramp.c60
-rw-r--r--generator/ppmmake.c18
-rw-r--r--lib/Makefile14
-rw-r--r--lib/libpamread.c51
-rw-r--r--lib/libpgm1.c60
-rw-r--r--lib/libpnm1.c20
-rw-r--r--lib/libppm1.c64
-rw-r--r--lib/standardppmdfont.c2
-rw-r--r--other/Makefile6
-rw-r--r--other/pamchannel.c10
-rw-r--r--other/pamfix.c291
-rwxr-xr-xother/pamfixtrunc50
-rw-r--r--other/pamfixtrunc.c176
-rw-r--r--other/pamvalidate.c86
-rwxr-xr-xtest/411toppm.test5
-rwxr-xr-xtest/Execute-Tests20
-rw-r--r--test/Test-Order6
-rwxr-xr-xtest/atk-roundtrip.test6
-rwxr-xr-xtest/avs-roundtrip.test9
-rwxr-xr-xtest/bmp-roundtrip.test8
-rwxr-xr-xtest/cmuw-roundtrip.test6
-rwxr-xr-xtest/cut-paste-roundtrip.test8
-rwxr-xr-xtest/eyuvtoppm.test5
-rwxr-xr-xtest/facesaver-roundtrip.test9
-rwxr-xr-xtest/fits-roundtrip.test6
-rwxr-xr-xtest/g3-roundtrip.test17
-rwxr-xr-xtest/gem-roundtrip.test6
-rwxr-xr-xtest/gif-quant-roundtrip.test9
-rw-r--r--test/gif-roundtrip.ok7
-rwxr-xr-xtest/gif-roundtrip.test51
-rwxr-xr-xtest/hdiff-roundtrip.test8
-rw-r--r--test/jbig-roundtrip.ok2
-rwxr-xr-xtest/jbig-roundtrip.test14
-rwxr-xr-xtest/leaf-roundtrip.test6
-rwxr-xr-xtest/mgr-roundtrip.test6
-rwxr-xr-xtest/mrf-roundtrip.test6
-rwxr-xr-xtest/pad-crop-roundtrip.test12
-rwxr-xr-xtest/pamchannel.test16
-rwxr-xr-xtest/pamcut.test12
-rwxr-xr-xtest/pamdepth-roundtrip.test12
-rwxr-xr-xtest/pamdice-roundtrip.test8
-rwxr-xr-xtest/pamditherbw.test20
-rwxr-xr-xtest/pamedge.test14
-rwxr-xr-xtest/pamenlarge.test12
-rwxr-xr-xtest/pamfile.test10
-rwxr-xr-xtest/pamflip-roundtrip.test61
-rwxr-xr-xtest/pamflip.test15
-rwxr-xr-xtest/pamseq.test5
-rw-r--r--test/pamslice-roundtrip.ok4
-rwxr-xr-xtest/pamslice-roundtrip.test69
-rwxr-xr-xtest/pamsumm.test7
-rwxr-xr-xtest/pamtopam.test11
-rwxr-xr-xtest/pbmclean.test12
-rwxr-xr-xtest/pbmmake.test11
-rwxr-xr-xtest/pbmminkowski.test13
-rwxr-xr-xtest/pbmpage.test9
-rwxr-xr-xtest/pbmpscale.test11
-rwxr-xr-xtest/pbmtext.test5
-rwxr-xr-xtest/pbmtog3.test30
-rwxr-xr-xtest/pbmupc.test5
-rwxr-xr-xtest/pfm-roundtrip.test9
-rwxr-xr-xtest/pgmcrater.test5
-rwxr-xr-xtest/pgmhist.test10
-rwxr-xr-xtest/pgmmake.test9
-rwxr-xr-xtest/pgmnoise.test5
-rw-r--r--test/pgmramp.ok42
-rwxr-xr-xtest/pgmramp.test14
-rwxr-xr-xtest/pgmtopgm.test5
-rwxr-xr-xtest/pgmtoppm.test16
-rw-r--r--test/png-roundtrip.ok34
-rwxr-xr-xtest/png-roundtrip.test50
-rwxr-xr-xtest/pnm-pam-roundtrip.test8
-rwxr-xr-xtest/pnm-plain-roundtrip.test11
-rwxr-xr-xtest/pnmcat.test11
-rwxr-xr-xtest/pnminvert-roundtrip.test7
-rwxr-xr-xtest/pnminvert.test21
-rwxr-xr-xtest/pnmpsnr.test12
-rwxr-xr-xtest/pnmremap1.test12
-rwxr-xr-xtest/pnmremap2.test16
-rw-r--r--test/pnmshear.ok2
-rwxr-xr-x[-rw-r--r--]test/pnmshear.test31
-rwxr-xr-xtest/pnmtile.test12
-rwxr-xr-xtest/pnmtopnm-plain.test11
-rwxr-xr-xtest/ppmbrighten.test9
-rwxr-xr-xtest/ppmchange-roundtrip.test18
-rwxr-xr-xtest/ppmcie.test11
-rwxr-xr-xtest/ppmdfont.test9
-rwxr-xr-xtest/ppmdim.test14
-rwxr-xr-xtest/ppmdither.test9
-rwxr-xr-xtest/ppmforge.test5
-rwxr-xr-xtest/ppmgauss.test5
-rwxr-xr-xtest/ppmhist.test8
-rwxr-xr-xtest/ppmmake.test9
-rwxr-xr-xtest/ppmmix.test30
-rwxr-xr-xtest/ppmpat.test15
-rw-r--r--test/ppmrelief.ok3
-rwxr-xr-xtest/ppmrelief.test42
-rwxr-xr-xtest/ppmrough.test5
-rwxr-xr-xtest/ppmtopgm.test5
-rwxr-xr-xtest/ppmtoppm.test5
-rwxr-xr-xtest/ppmwheel.test7
-rwxr-xr-xtest/ps-alt-roundtrip.test26
-rwxr-xr-xtest/ps-roundtrip.test45
-rwxr-xr-xtest/rgb3-roundtrip.test19
-rwxr-xr-xtest/sunrast-roundtrip.test6
-rw-r--r--test/symmetry.ok12
-rwxr-xr-xtest/symmetry.test95
-rwxr-xr-xtest/targa-roundtrip.test20
-rwxr-xr-xtest/tiff-roundtrip.test24
-rwxr-xr-xtest/utahrle-roundtrip.test15
-rwxr-xr-xtest/wbmp-roundtrip.test6
-rwxr-xr-xtest/winicon-roundtrip.test25
-rwxr-xr-xtest/xbm-roundtrip.test8
-rwxr-xr-xtest/xpm-roundtrip.test14
-rwxr-xr-xtest/xwd-roundtrip.test20
-rwxr-xr-xtest/yuv-roundtrip.test9
-rw-r--r--version.mk4
138 files changed, 3551 insertions, 1231 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 864adea6..8cda072d 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -260,10 +260,10 @@ endif
 
 .PHONY: install-merge install-nonmerge
 install-merge: install.merge install.lib install.data \
-	install.manweb install.man
+	install.manwebmain install.manweb install.man
 
 install-nonmerge: install.bin install.lib install.data \
-	install.manweb install.man
+	install.manwebmain install.manweb install.man
 
 .PHONY: merge
 merge: lib/all netpbm
@@ -369,7 +369,7 @@ install.lib:
 endif
 
 .PHONY: install.manweb
-install.manweb: $(PKGDIR)/man/web/netpbm.url $(PKGDIR)/bin/doc.url
+install.manwebmain: $(PKGDIR)/man/web/netpbm.url $(PKGDIR)/bin/doc.url
 
 $(PKGDIR)/man/web/netpbm.url: $(PKGDIR)/man/web
 	echo "$(NETPBM_DOCURL)" > $@
@@ -418,6 +418,12 @@ install.sharedlibstub:
 	$(MAKE) -C lib -f $(SRCDIR)/lib/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) install.sharedlibstub 
 
+# Make the 'deb' target after making 'package'.  It generates a .deb
+# file in the current directory.
+.PHONY: deb
+deb:
+	buildtools/debian/mkdeb --buildtools=buildtools --pkgdir=$(PKGDIR)
+
 .PHONY: check
 check:
 # This works on typical Linux systems
@@ -434,6 +440,7 @@ clean: localclean
 localclean:
 	rm -f netpbm build_started build_complete
 	rm -f pm_config.h inttypes_netpbm.h version.h
+	rm -f *.deb
 
 # Note that removing config.mk must be the last thing we do,
 # because no other makes will work after that is done.
diff --git a/analyzer/pgmhist.c b/analyzer/pgmhist.c
index f3a67383..1e779655 100644
--- a/analyzer/pgmhist.c
+++ b/analyzer/pgmhist.c
@@ -20,7 +20,7 @@
 
 
 
-struct cmdline_info {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -29,13 +29,14 @@ struct cmdline_info {
     unsigned int median;
     unsigned int quartile;
     unsigned int decile;
+    unsigned int forensic;
 };
 
 
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdline_info * 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.
@@ -43,7 +44,7 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;  /* set by OPTENT3 */
     optEntry * option_def;
     unsigned int option_def_index;
-    
+
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
@@ -56,6 +57,8 @@ parseCommandLine(int argc, const char ** argv,
             &cmdlineP->quartile,            0);
     OPTENT3(0,   "decile",        OPT_FLAG,  NULL,
             &cmdlineP->decile,              0);
+    OPTENT3(0,   "forensic",      OPT_FLAG,  NULL,
+            &cmdlineP->forensic,            0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -68,7 +71,7 @@ parseCommandLine(int argc, const char ** argv,
         pm_error("You may specify only one of -median, -quartile, "
                  "and -decile");
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -81,39 +84,80 @@ parseCommandLine(int argc, const char ** argv,
 
 
 
+static gray
+universalMaxval(gray const maxval,
+                int  const format) {
+/*----------------------------------------------------------------------------
+  A maxval that makes it impossible for a pixel to be invalid in an image that
+  states it maxval as 'maxval' and has format 'format'.
+
+  E.g. in a one-byte-per-sample image, it's not possible to read a sample
+  value greater than 255, so a maxval of 255 makes it impossible for a sample
+  to be invalid.
+
+  But: we never go above 65535, which means our maxval isn't entirely
+  universal.  If the image is plain PGM, it could contain a pixel that
+  exceeds even that.
+-----------------------------------------------------------------------------*/
+    assert(0 < maxval && maxval < 65536);
+
+    if (format == RPGM_FORMAT) {
+        /*  Raw PGM stream has either one or two bytes per pixel, depending
+            upon its stated maxval.
+        */
+        if (maxval > 255)
+            return 65535;
+        else
+            return 255;
+    } else if (format == RPBM_FORMAT) {
+        /* A Raw PBM stream has one bit per pixel, which libnetpbm renders
+           as 0 or 255 when we read it.
+        */
+        assert(maxval == 255);
+        return 255;
+    } else {
+        /* A plain PGM or PBM stream has essentially unlimited range in the
+           tokens that are supposed to be sample values.  We arbitrarily draw
+           the line at 65535.
+        */
+        return 65535;
+    }
+}
+
+
+
 static void
-buildHistogram(FILE *          const ifP,
-               unsigned int ** const histP,
-               gray *          const maxvalP) {
+buildHistogram(FILE *               const ifP,
+               int                  const format,
+               unsigned int         const cols,
+               unsigned int         const rows,
+               gray                 const mmaxval,
+               unsigned long int ** const histP) {
+/*----------------------------------------------------------------------------
+   Compute the histogram of sample values in the input stream *ifP as *histP,
+   in newly malloced storage.
 
+   Assume the image maxval is 'mmaxval'.  Assume *ifP is positioned to the
+   start of the raster.
+-----------------------------------------------------------------------------*/
     gray * grayrow;
-    int rows, cols;
-    int format;
     unsigned int row;
     unsigned int i;
-    unsigned int * hist;  /* malloc'ed array */
-    gray maxval;
-
-    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
-
-    if (UINT_MAX / cols < rows)
-        pm_error("Too many pixels (%u x %u) in image.  "
-                 "Maximum computable is %u",
-                 cols, rows, UINT_MAX);
+    unsigned long int * hist;  /* malloc'ed array */
 
     grayrow = pgm_allocrow(cols);
 
-    MALLOCARRAY(hist, maxval + 1);
+    MALLOCARRAY(hist, mmaxval + 1);
     if (hist == NULL)
         pm_error("out of memory");
 
-    for (i = 0; i <= maxval; ++i)
+    for (i = 0; i <= mmaxval; ++i)
         hist[i] = 0;
 
     for (row = 0; row < rows; ++row) {
         unsigned int col;
 
-        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+        pgm_readpgmrow(ifP, grayrow, cols, mmaxval, format);
 
         for (col = 0; col < cols; ++col) {
             /* Because total pixels in image is limited: */
@@ -124,49 +168,33 @@ buildHistogram(FILE *          const ifP,
     }
     pgm_freerow(grayrow);
 
-    *histP   = hist;
-    *maxvalP = maxval;
-}
-
-
-
-static unsigned int
-sum(unsigned int const hist[],
-    gray         const maxval) {
-
-    unsigned int sum;
-    unsigned int sampleVal;
-
-    for (sampleVal = 0, sum = 0; sampleVal <= maxval; ++sampleVal)
-        sum += hist[sampleVal];
-
-    return sum;
+    *histP = hist;
 }
 
 
 
 static void
-findQuantiles(unsigned int const n,
-              unsigned int const hist[],
-              gray         const maxval,
-              gray *       const quantile) {
+findQuantiles(unsigned int      const n,
+              unsigned long int const hist[],
+              unsigned long int const totalCt,
+              gray              const mmaxval,
+              gray *            const quantile) {
 /*----------------------------------------------------------------------------
    Find the order-n quantiles (e.g. n == 4 means quartiles) of the pixel
    sample values, given that hist[] is the histogram of them (hist[N] is the
    number of pixels that have sample value N).
 
-   'maxval' is the maxval of the image, so the size of hist[] is 'maxval' + 1.
+   'mmaxval' is the highest index in hist[] (so its size is 'mmaxval' + 1,
+   and there are no pixels greater than 'mmaxval' in the image).
 
    We return the ith quantile as quantile[i].  For example, for quartiles,
    quantile[3] is the least sample value for which at least 3/4 of the pixels
-   are less than or equal to it.  
+   are less than or equal to it.
 
    quantile[] must be allocated at least to size 'n'.
 
-   We return 
+   'n' must not be more than 100.
 -----------------------------------------------------------------------------*/
-    unsigned int const totalCt = sum(hist, maxval);
-
     unsigned int quantSeq;
         /* 0 is first quantile, 1 is second quantile, etc. */
 
@@ -183,9 +211,9 @@ findQuantiles(unsigned int const n,
     cumCt = hist[0];  /* initial value */
 
     for (quantSeq = 1; quantSeq <= n; ++quantSeq) {
-        unsigned long int const q = totalCt/n; 
-        unsigned long int const r = totalCt%n;  
-        unsigned long int const quantCt = q*quantSeq + (r*quantSeq +n -1)/n;
+        unsigned long int const q = totalCt / n; 
+        unsigned long int const r = totalCt % n;  
+        unsigned long int const quantCt = q*quantSeq + (r*quantSeq + n - 1)/n;
        /* This is how many pixels are (ignoring quantization) in the
           quantile.  E.g. for the 3rd quartile, it is 3/4 of the pixels
           in the image.
@@ -195,14 +223,11 @@ findQuantiles(unsigned int const n,
           for preventing overflow for slight innacuracies in floating
           point arithmetic causes problems when used as loop counter
           and array index.
-
-          The maximum value for n is currently 10; in the future this
-          may be brought up to 100.
        */
         assert(quantCt <= totalCt);
 
-        /* at sampleVal == maxval, cumCt == totalCt, so because
-           quantCt <= 'totalCt', 'sampleVal' cannot go above maxval.
+        /* at sampleVal == mmaxval, cumCt == totalCt, so because
+           quantCt <= 'totalCt', 'sampleVal' cannot go above mmaxval.
         */
 
         while (cumCt < quantCt) {
@@ -210,7 +235,7 @@ findQuantiles(unsigned int const n,
             cumCt += hist[sampleVal];
         }
 
-        assert(sampleVal <= maxval);
+        assert(sampleVal <= mmaxval);
 
         /* 'sampleVal' is the lowest sample value for which at least 'quantCt'
            pixels have that sample value or less.  'cumCt' is the number
@@ -223,9 +248,10 @@ findQuantiles(unsigned int const n,
 
 
 static void
-countCumulative(unsigned int    const hist[],
-                gray            const maxval,
-                unsigned int ** const rcountP) {
+countCumulative(unsigned long int    const hist[],
+                gray                 const mmaxval,
+                unsigned long int    const totalPixelCt,
+                unsigned long int ** const rcountP) {
 /*----------------------------------------------------------------------------
    From the histogram hist[] (hist[N] is the number of pixels of sample
    value N), compute the cumulative distribution *rcountP ((*rcountP)[N]
@@ -233,17 +259,17 @@ countCumulative(unsigned int    const hist[],
 
    *rcountP is newly malloced memory.
 -----------------------------------------------------------------------------*/
-    unsigned int * rcount;
-    unsigned int cumCount;
+    unsigned long int * rcount;
+    unsigned long int cumCount;
     int i;
-    
-    MALLOCARRAY(rcount, maxval + 1);
+
+    MALLOCARRAY(rcount, mmaxval + 1);
     if (rcount == NULL)
         pm_error("out of memory");
 
-    for (i = maxval, cumCount = 0; i >= 0; --i) {
+    for (i = mmaxval, cumCount = 0; i >= 0; --i) {
         /* Because total pixels in image is limited: */
-        assert(UINT_MAX - hist[i] >= cumCount);
+        assert(ULONG_MAX - hist[i] >= cumCount);
 
         cumCount += hist[i];
         rcount[i] = cumCount;
@@ -255,11 +281,11 @@ countCumulative(unsigned int    const hist[],
 
 
 static void
-reportHistHumanFriendly(unsigned int const hist[],
-                        unsigned int const rcount[],
-                        gray         const maxval) {
+reportHistHumanFriendly(unsigned long int const hist[],
+                        unsigned long int const rcount[],
+                        gray              const maxval) {
 
-    unsigned int const totalPixels = rcount[0];
+    unsigned long int const totalPixelCt = rcount[0];
 
     unsigned int cumCount;
     unsigned int i;
@@ -271,9 +297,48 @@ reportHistHumanFriendly(unsigned int const hist[],
         if (hist[i] > 0) {
             cumCount += hist[i];
             printf(
-                "%5d  %5d  %5.3g%%  %5.3g%%\n", i, hist[i],
-                (float) cumCount * 100.0 / totalPixels, 
-                (float) rcount[i] * 100.0 / totalPixels);
+                "%5d  %5ld  %5.3g%%  %5.3g%%\n", i, hist[i],
+                (float) cumCount * 100.0 / totalPixelCt,
+                (float) rcount[i] * 100.0 / totalPixelCt);
+        }
+    }
+}
+
+
+static void
+reportHistForensicHumanFriendly(unsigned long int const hist[],
+                                unsigned long int const rcount[],
+                                gray              const maxval,
+                                gray              const mmaxval) {
+
+    unsigned long int const totalPixelCt = rcount[0];
+
+    unsigned long int cumCount;
+    unsigned int i;
+
+    printf("value  count  b%%     w%%   \n");
+    printf("-----  -----  ------  ------\n");
+
+    for (i = 0, cumCount = 0; i <= maxval; ++i) {
+        if (hist[i] > 0) {
+            cumCount += hist[i];
+            printf(
+                "%5d  %5ld  %5.3g%%  %5.3g%%\n", i, hist[i],
+                (float) cumCount * 100.0 / totalPixelCt,
+                (float) rcount[i] * 100.0 / totalPixelCt);
+        }
+    }
+    if (totalPixelCt > cumCount) {
+        printf("-----  -----\n");
+
+        for (i = maxval; i <= mmaxval; ++i) {
+            if (hist[i] > 0) {
+                cumCount += hist[i];
+                printf(
+                    "%5d  %5ld  %5.3g%%  %5.3g%%\n", i, hist[i],
+                    (float) cumCount * 100.0 / totalPixelCt,
+                    (float) rcount[i] * 100.0 / totalPixelCt);
+            }
         }
     }
 }
@@ -281,13 +346,13 @@ reportHistHumanFriendly(unsigned int const hist[],
 
 
 static void
-reportHistMachineFriendly(unsigned int const hist[],
-                          gray         const maxval) {
+reportHistMachineFriendly(unsigned long int const hist[],
+                          gray              const maxval) {
 
     unsigned int i;
 
     for (i = 0; i <= maxval; ++i) {
-        printf("%u %u\n", i, hist[i]);
+        printf("%u %lu\n", i, hist[i]);
     }
 }
 
@@ -345,60 +410,132 @@ reportDecilesHumanFriendly(gray const decile[]) {
 
 
 
-int
-main(int argc, const char ** argv) {
+static void
+summarizeInvalidPixels(unsigned long int const hist[],
+                       unsigned long int const rcount[],
+                       gray              const mmaxval,
+                       gray              const maxval) {
+/*----------------------------------------------------------------------------
+  Print total count of valid and invalid pixels, if there are any
+  invalid ones.
+-----------------------------------------------------------------------------*/
+    unsigned long int const invalidPixelCt = 
+        mmaxval > maxval ? rcount[maxval+1] : 0;
+
+    if (invalidPixelCt > 0) {
+        unsigned long int const totalPixelCt = rcount[0];
+        unsigned long int const validPixelCt = totalPixelCt - invalidPixelCt;
+
+        printf("\n");
+        printf("** Image stream contains invalid sample values "
+               "(above maxval %u)\n", maxval);
+        printf("Valid sample values:   %lu (%5.4g%%)\n",
+               validPixelCt,   (float)validPixelCt / totalPixelCt * 100.0);
+        printf("Invalid sample values: %lu (%5.4g%%)\n",
+               invalidPixelCt, (float)invalidPixelCt / totalPixelCt * 100.0);
+    }
+}
 
-    struct cmdline_info cmdline;
-    FILE * ifP;
-    gray maxval;
-    unsigned int * hist;   /* malloc'ed array */
 
-    pm_proginit(&argc, argv);
 
-    parseCommandLine(argc, argv, &cmdline);
+static void
+reportFromHistogram(const unsigned long int * const hist,
+                    gray                      const mmaxval,
+                    gray                      const maxval,
+                    unsigned long int         const totalPixelCt,
+                    struct CmdlineInfo        const cmdline) {
+/*----------------------------------------------------------------------------
+   Analyze histogram 'hist', which has 'mmaxval' buckets, and report
+   what we find.
 
-    ifP = pm_openr(cmdline.inputFileName);
+   'maxval' is the maxval that the image states (but note that we tolerate
+   invalid sample values greater than maxval, which could be as high as
+   'mmaxval').
 
-    buildHistogram(ifP, &hist, &maxval);
+   'cmdline' tells what kind of reporting to do.
+-----------------------------------------------------------------------------*/
 
     if (cmdline.median) {
         gray median[2];
-        findQuantiles(2, hist, maxval, median); 
+        findQuantiles(2, hist, totalPixelCt, mmaxval, median);
         if (cmdline.machine)
             reportQuantilesMachineFriendly(median, 1);
         else
             reportMedianHumanFriendly(median[0]);
     } else if (cmdline.quartile) {
         gray quartile[4];
-        findQuantiles(4, hist, maxval, quartile);
+        findQuantiles(4, hist, totalPixelCt, mmaxval, quartile);
         if (cmdline.machine)
             reportQuantilesMachineFriendly(quartile, 4);
         else
             reportQuartilesHumanFriendly(quartile);
     } else if (cmdline.decile) {
         gray decile[10];
-        findQuantiles(10, hist, maxval, decile);
+        findQuantiles(10, hist, totalPixelCt, mmaxval, decile);
         if (cmdline.machine)
             reportQuantilesMachineFriendly(decile, 10);
         else
             reportDecilesHumanFriendly(decile);
     } else {
         if (cmdline.machine)
-            reportHistMachineFriendly(hist, maxval);
+            reportHistMachineFriendly(hist, mmaxval);
         else {
-            unsigned int * rcount; /* malloc'ed array */
-            countCumulative(hist, maxval, &rcount);
-            reportHistHumanFriendly(hist, rcount, maxval);
+            unsigned long int * rcount; /* malloc'ed array */
+            countCumulative(hist, mmaxval, totalPixelCt, &rcount);
+            if (cmdline.forensic)
+                reportHistForensicHumanFriendly(hist, rcount, maxval, mmaxval);
+            else
+                reportHistHumanFriendly(hist, rcount, maxval);
+
+            summarizeInvalidPixels(hist, rcount, mmaxval, maxval);
 
             free(rcount);
         }
     }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    int rows, cols;
+    int format;
+    gray maxval;
+        /* Stated maxval of the image */
+    gray mmaxval;
+        /* Maxval we assume, which may be greater than the stated maxval
+           so that we can process invalid pixels in the image that exceed
+           the maxval.
+        */
+    unsigned long int totalPixelCt;
+    unsigned long int * hist;   /* malloc'ed array */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+
+    if (ULONG_MAX / cols < rows)
+        pm_error("Too many pixels (%u x %u) in image.  "
+                 "Maximum computable is %lu",
+                 cols, rows, ULONG_MAX);
+
+    totalPixelCt = cols * rows;
+
+    mmaxval = cmdline.forensic ? universalMaxval(maxval, format) : maxval;
+
+    buildHistogram(ifP, format, cols, rows, mmaxval, &hist);
+
+    reportFromHistogram(hist, mmaxval, maxval, totalPixelCt, cmdline);
 
     free(hist);
     pm_close(ifP);
 
     return 0;
 }
-
-
-
diff --git a/analyzer/ppmhist.c b/analyzer/ppmhist.c
index 2f6c9348..cc47bb82 100644
--- a/analyzer/ppmhist.c
+++ b/analyzer/ppmhist.c
@@ -20,17 +20,18 @@
 
 enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB};
 
-enum colorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN};
+enum ColorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN};
 
 struct cmdline_info {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char * inputFileName;  /* Name of input file */
-    unsigned int noheader;    /* -noheader option */
-    enum colorFmt colorFmt;
-    unsigned int colorname;   /* -colorname option */
-    enum sort sort;           /* -sort option */
+    unsigned int noheader;
+    enum ColorFmt colorFmt;
+    unsigned int colorname;
+    enum sort sort;
+    unsigned int forensic;
 };
 
 
@@ -45,7 +46,7 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;  /* set by OPTENT3 */
     optEntry * option_def;
     unsigned int option_def_index;
-    
+
     unsigned int hexcolorOpt, floatOpt, mapOpt, nomapOpt;
     const char * sort_type;
 
@@ -59,6 +60,7 @@ parseCommandLine(int argc, const char ** argv,
     OPTENT3(0,   "float",     OPT_FLAG, NULL,  &floatOpt,              0);
     OPTENT3(0,   "colorname", OPT_FLAG, NULL,  &cmdlineP->colorname,   0);
     OPTENT3(0,   "sort",      OPT_STRING, &sort_type, NULL,            0);
+    OPTENT3(0,   "forensic",  OPT_FLAG, NULL,  &cmdlineP->forensic,    0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -69,8 +71,9 @@ parseCommandLine(int argc, const char ** argv,
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -84,9 +87,12 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->colorFmt = FMT_HEX;
     else if (floatOpt)
         cmdlineP->colorFmt = FMT_FLOAT;
-    else if (mapOpt)
+    else if (mapOpt) {
+        if (cmdlineP->forensic)
+            pm_error("You cannot specify -map and -forensic together");
+
         cmdlineP->colorFmt = FMT_PPMPLAIN;
-    else 
+    } else
         cmdlineP->colorFmt = FMT_DECIMAL;
 
     if (strcmp(sort_type, "frequency") == 0)
@@ -161,8 +167,137 @@ rgbcompare(const void * const a,
 
 
 
+static pixval
+universalMaxval(pixval const maxval,
+                int    const format) {
+/*----------------------------------------------------------------------------
+  A maxval that makes it impossible for a pixel to be invalid in an image that
+  states it maxval as 'maxval' and has format 'format'.
+
+  E.g. in a one-byte-per-sample image, it's not possible to read a sample
+  value greater than 255, so a maxval of 255 makes it impossible for a sample
+  to be invalid.
+
+  But: we never go above 65535, which means our maxval isn't entirely
+  universal.  If the image is plain PPM, it could contain a pixel that
+  exceeds even that.
+-----------------------------------------------------------------------------*/
+    assert(0 < maxval && maxval < 65536);
+
+    if (format == RPPM_FORMAT || format == RPGM_FORMAT) {
+        /*  Raw PPM stream has either one or two bytes per pixel, depending
+            upon its stated maxval.
+        */
+        if (maxval > 255)
+            return 65535;
+        else
+            return 255;
+    } else if (format == RPBM_FORMAT) {
+        /* A Raw PBM stream has one bit per pixel, which libnetpbm renders
+           as 0 or 255 when we read it.
+        */
+        assert(maxval == 255);
+        return 255;
+    } else {
+        /* A plain PPM stream has essentially unlimited range in the
+           tokens that are supposed to be sample values.  We arbitrarily draw
+           the line at 65535.
+        */
+        return 65535;
+    }
+}
+
+
+
+static bool
+colorIsValid(pixel  const color,
+             pixval const maxval) {
+
+    pixval const r = PPM_GETR(color);
+    pixval const g = PPM_GETG(color);
+    pixval const b = PPM_GETB(color);
+
+    return r <= maxval && g <= maxval && b <= maxval;
+}
+
+
+
+static void
+separateInvalidItems(colorhist_vector const chv,
+                     colorhist_vector const chvInvalid,
+                     pixval           const maxval,
+                     unsigned int     const colorCt,
+                     unsigned int  *  const validColorCtP) {
+/*----------------------------------------------------------------------------
+  Move invalid color entries from chv to chvInvalid.
+  Count how many color entries are valid. 
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int validCt;
+    unsigned int invalidCt;
+ 
+    for (i = 0, validCt = 0, invalidCt = 0; i < colorCt; ++i) {
+        if (!colorIsValid(chv[i].color, maxval))
+            chvInvalid[invalidCt++] = chv[i];
+        else
+            chv[validCt++] = chv[i];
+    } 
+    *validColorCtP = validCt;
+}
+  
+
+
+static void
+sortHistogramForensic(enum sort        const sortFn,
+                      colorhist_vector const chv,
+                      colorhist_vector const chvInvalid,
+                      pixval           const maxval,
+                      unsigned int     const colorCt,
+                      unsigned int *   const validColorCtP) {
+
+    unsigned int validColorCt;
+
+    separateInvalidItems(chv, chvInvalid, maxval, colorCt, &validColorCt);
+
+    {
+        int (*compare_function)(const void *, const void *);
+    
+        switch (sortFn) {
+        case SORT_BY_FREQUENCY: compare_function = countcompare; break;
+        case SORT_BY_RGB:       compare_function = rgbcompare;   break;
+        }
+
+        qsort((void*) chv, validColorCt,
+              sizeof(struct colorhist_item), compare_function);
+        
+        qsort((void*) chvInvalid, colorCt - validColorCt,
+              sizeof(struct colorhist_item), compare_function);
+    }
+    *validColorCtP = validColorCt;
+}
+
+
+
+static void
+sortHistogramNormal(enum sort        const sortFn,
+                    colorhist_vector const chv,
+                    unsigned int     const colorCt) {
+
+    int (*compare_function)(const void *, const void *);
+
+    switch (sortFn) {
+    case SORT_BY_FREQUENCY: compare_function = countcompare; break;
+    case SORT_BY_RGB:       compare_function = rgbcompare;   break;
+    }
+
+    qsort((void*) chv, colorCt, sizeof(struct colorhist_item),
+          compare_function);
+}
+
+
+
 static const char *
-colornameLabel(pixel        const color, 
+colornameLabel(pixel        const color,
                pixval       const maxval,
                unsigned int const nDictColor,
                pixel        const dictColors[],
@@ -172,15 +307,15 @@ colornameLabel(pixel        const color,
    dictionary to it.  If the name returned is not the exact color,
    prefix it with "*".  Otherwise, prefix it with " ".
 
-   'nDictColor', dictColors[], and dictColorNames[] are the color 
+   'nDictColor', dictColors[], and dictColorNames[] are the color
    dictionary.
 
    Return the name in static storage within this subroutine.
 -----------------------------------------------------------------------------*/
     static char retval[32];
     int colorIndex;
-    
-    pixel color255;  
+
+    pixel color255;
         /* The color, normalized to a maxval of 255: the maxval of a color
            dictionary.
         */
@@ -190,31 +325,31 @@ colornameLabel(pixel        const color,
     colorIndex = ppm_findclosestcolor(dictColors, nDictColor, &color);
 
     assert(colorIndex >= 0 && colorIndex < nDictColor);
-    
+
     if (PPM_EQUAL(dictColors[colorIndex], color))
         STRSCPY(retval, " ");
     else
         STRSCPY(retval, "*");
-    
+
     STRSCAT(retval, dictColornames[colorIndex]);
-    
+
     return retval;
 }
-                
+
 
 
 static void
-printColors(colorhist_vector const chv, 
-            int              const nColors,
+printColors(colorhist_vector const chv,
+            int              const colorCt,
             pixval           const maxval,
-            enum colorFmt    const colorFmt,
+            enum ColorFmt    const colorFmt,
             unsigned int     const nKnown,
             pixel            const knownColors[],
             const char *     const colornames[]) {
 
     int i;
 
-    for (i = 0; i < nColors; i++) {
+    for (i = 0; i < colorCt; i++) {
         pixval       const r          = PPM_GETR(chv[i].color);
         pixval       const g          = PPM_GETG(chv[i].color);
         pixval       const b          = PPM_GETB(chv[i].color);
@@ -226,7 +361,7 @@ printColors(colorhist_vector const chv,
         const char * colornameValue;
 
         if (colornames)
-            colornameValue = colornameLabel(chv[i].color, maxval, 
+            colornameValue = colornameLabel(chv[i].color, maxval,
                                             nKnown, knownColors, colornames);
         else
             colornameValue = "";
@@ -257,23 +392,97 @@ printColors(colorhist_vector const chv,
 
 
 
+static void
+summarizeInvalidPixels(unsigned long int const validPixelCt,
+                       unsigned long int const invalidPixelCt,
+                       pixval            const maxval) {
+/*----------------------------------------------------------------------------
+  Print total count of valid and invalid pixels, if there are any
+  invalid ones.
+-----------------------------------------------------------------------------*/
+    if (invalidPixelCt > 0) {
+        unsigned long int const totalPixelCt = validPixelCt + invalidPixelCt;
+
+        printf("\n");
+        printf("** Image stream contains invalid sample values "
+               "(above maxval %u)\n", maxval);
+        printf("** Valid sample values : %lu (%5.4g%%)\n",
+               validPixelCt,   (float) validPixelCt   / totalPixelCt * 100.0);
+        printf("** Invalid sample values : %lu (%5.4g%%)\n",
+               invalidPixelCt, (float) invalidPixelCt / totalPixelCt * 100.0);
+    }
+}
+
+
+
+static void
+printInvalidSamples(colorhist_vector const chv,
+                    colorhist_vector const chvInvalid,
+                    int              const totalColorCt,
+                    unsigned int     const validColorCt,
+                    pixval           const maxval,
+                    enum ColorFmt    const colorFmt) {
+
+    unsigned int const invalidColorCt = totalColorCt - validColorCt;
+
+    unsigned int i;
+    unsigned long int validPixelCt;
+    unsigned long int invalidPixelCt;
+
+    for (i = 0, validPixelCt = 0; i < validColorCt; ++i)
+        validPixelCt += chv[i].value; 
+
+    for (i = 0, invalidPixelCt = 0; i < invalidColorCt; ++i) {
+        pixval       const r     = PPM_GETR(chvInvalid[i].color);
+        pixval       const g     = PPM_GETG(chvInvalid[i].color);
+        pixval       const b     = PPM_GETB(chvInvalid[i].color);
+        unsigned int const count = chvInvalid[i].value;
+
+        invalidPixelCt += chvInvalid[i].value; 
+
+        switch(colorFmt) {
+        case FMT_FLOAT:
+            printf(" %1.3f %1.3f %1.3f\t\t%7d\n",
+                   (double)r / maxval,
+                   (double)g / maxval,
+                   (double)b / maxval,
+                   count);
+            break;
+        case FMT_HEX:
+            printf("  %04x  %04x  %04x\t\t%7d\n",
+                   r, g, b, count);
+            break;
+        case FMT_DECIMAL:
+            printf(" %5d %5d %5d\t\t%7d\n",
+                   r, g, b, count);
+            break;
+        case FMT_PPMPLAIN:
+            assert(false);
+            break;
+        }
+    }
+
+    summarizeInvalidPixels(validPixelCt, invalidPixelCt, maxval);
+}
+
+
+
 int
 main(int argc, const char *argv[]) {
 
     struct cmdline_info cmdline;
     FILE * ifP;
     colorhist_vector chv;
+    colorhist_vector chvInvalid;
     int rows, cols;
     pixval maxval;
+    pixval mmaxval;
     int format;
-    int nColors;
-    int (*compare_function)(const void *, const void *);
-        /* The compare function to be used with qsort() to sort the
-           histogram for output
-        */
-    unsigned int nDictColor;
+    int colorCt;
+    unsigned int dictColorCt;
     const char ** dictColornames;
     pixel * dictColors;
+    unsigned int validColorCt;
 
     pm_proginit(&argc, argv);
 
@@ -283,23 +492,29 @@ main(int argc, const char *argv[]) {
 
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
-    chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, 0, &nColors);
+    mmaxval = cmdline.forensic ? universalMaxval(maxval, format) : maxval;
+
+    chv = ppm_computecolorhist2(ifP, cols, rows, mmaxval, format, 0, &colorCt);
 
     pm_close(ifP);
 
-    switch (cmdline.sort) {
-    case SORT_BY_FREQUENCY:
-        compare_function = countcompare; break;
-    case SORT_BY_RGB:
-        compare_function = rgbcompare; break;
-    }
+    /* Sort and produce histogram. */
+    if (cmdline.forensic) {
+        MALLOCARRAY(chvInvalid, colorCt);
+        if (chvInvalid == NULL)
+            pm_error("out of memory generating histogram");
 
-    qsort((char*) chv, nColors, sizeof(struct colorhist_item), 
-          compare_function);
+        sortHistogramForensic(cmdline.sort, chv, chvInvalid,
+                              maxval, colorCt, &validColorCt);
+    } else {
+        chvInvalid = NULL;
+        sortHistogramNormal(cmdline.sort, chv, colorCt);
+        validColorCt = colorCt;
+    }
 
     /* And print the histogram. */
-    if (cmdline.colorFmt == FMT_PPMPLAIN) 
-        printf("P3\n# color map\n%d 1\n%d\n", nColors, maxval);
+    if (cmdline.colorFmt == FMT_PPMPLAIN)
+        printf("P3\n# color map\n%d 1\n%d\n", colorCt, maxval);
 
     if (!cmdline.noheader) {
         const char commentDelim = cmdline.colorFmt == FMT_PPMPLAIN ? '#' : ' ';
@@ -309,16 +524,20 @@ main(int argc, const char *argv[]) {
                commentDelim, cmdline.colorname ? "----" : "");
     }
     if (cmdline.colorname) {
-        bool mustOpenTrue = TRUE;
-        ppm_readcolordict(NULL, mustOpenTrue, 
-                          &nDictColor, &dictColornames, &dictColors, NULL);
+        bool const mustOpenTrue = TRUE;
+        ppm_readcolordict(NULL, mustOpenTrue,
+                          &dictColorCt, &dictColornames, &dictColors, NULL);
     } else {
         dictColors = NULL;
         dictColornames = NULL;
     }
-        
-    printColors(chv, nColors, maxval,
-                cmdline.colorFmt, nDictColor, dictColors, dictColornames);
+
+    printColors(chv, validColorCt, maxval,
+                cmdline.colorFmt, dictColorCt, dictColors, dictColornames);
+
+    if (colorCt > validColorCt)
+        printInvalidSamples(chv, chvInvalid, colorCt, validColorCt,
+                            maxval, cmdline.colorFmt);
 
     if (dictColors)
         free(dictColors);
@@ -327,5 +546,11 @@ main(int argc, const char *argv[]) {
 
     ppm_freecolorhist(chv);
 
+    if (chvInvalid)
+        ppm_freecolorhist(chvInvalid);
+
     return 0;
 }
+
+
+
diff --git a/buildtools/README.pkg b/buildtools/README.pkg
index e544cbb7..f098e04b 100644
--- a/buildtools/README.pkg
+++ b/buildtools/README.pkg
@@ -111,15 +111,26 @@ The parts to be installed are:
 
 
     One of the Netpbm programs is Manweb, which is designed to be a
-    replacement for the classic Man program that can access both
-    traditional man pages and worldwide web documentation in the
-    Netpbm style with the familiar 'man jpegtopnm' kind of command.
-    To set up your system for this, you will have to be sure to create
-    the /usr/man/web directory, with 'netpbm.url' in it.  Also, If you
-    install Manweb as 'man', there is no point to installing the
+    replacement for the classic Man program that can access both traditional
+    man pages and worldwide web documentation in the Netpbm style with the
+    familiar 'man jpegtopnm' kind of command.  The package contains the files
+    necessary to use Manweb to access Netpbm documention on the web (on
+    netpbm.sourceforge.net).  These files are the contents of the man/web
+    directory and the file 'bin/doc.url'.  You should install the
+    'bin/doc.url' file if you are installing the Netpbm executables in a
+    directory all their own.  You should install the 'man/web' files if you
+    are installing the Netpbm executables in some global directory such as
+    'usr/bin'.  You should install neither of these if you don't intend to use
+    Manweb.
+
+    If you install Manweb as 'man', there is no point to installing the
     pointer man pages -- they will never be used.
-    
 
+    If you want to use Manweb to view the Netpbm manual but use a local
+    copy of the manual instead of accessing it on the web, you can get the
+    HTML files from the Sourceforge Subversion repository and install those
+    instead of the .url files that appear in the 'man/web' directory of the
+    package.
 
 The instructions above suggest putting the Netpbm parts in common
 directories such as /usr/bin, mingled with other packages.  This is
@@ -134,21 +145,34 @@ entire installation when you don't want it anymore, and to keep
 multiple versions around.
 
 
-netpbm.config
+netpbm.pkgconfig
+----------------
+
+You should create a file named 'netpbm.pkgconfig' out of the template file
+'pkgconfig_template' in the package directory, and install netpbm.pkgconfig in
+your pkg-config directory (typically /usr/lib/pkgconfig).  Programs that want
+to find out where you installed some part of Netpbm can use a 'pkg-config
+netpbm ...' command to find out.  For example, a make file for a program that
+uses the Netpbm programming library might use this facility to generate the
+necessary compiler and linker options to access that library.
+
+The pkg-config facility is fairly widely used, especially with things related
+to the X Window System.
+
+
+netpbm-config
 -------------
 
-You should create a shell script named 'netpbm.config' out of the
-template file 'config_template' in the package directory, and install
-netpbm.config in your executable search path.  Programs that want to
-find out where you installed some part of Netpbm can invoke
-netpbm.config and it will tell them.  For example, a make file for a
-program that uses the Netpbm programming library might use
-netpbm.config to generate the necessary compiler and linker options to
-access that library.
-
-Using netpbm.config, it's possible to have a viable Netpbm
-installation where netpbm.config is the only file in any default
-search path.
-
-The xxx.config file concept is a relatively new but growing convention,
-seen mostly in software related to the X Window System.
+You should create a shell script named 'netpbm-config' out of the template
+file 'config_template' in the package directory, and install netpbm-config in
+your executable search path.  Programs that want to find out where you
+installed some part of Netpbm can invoke netpbm-config and it will tell them.
+For example, a make file for a program that uses the Netpbm programming
+library might use netpbm-config to generate the necessary compiler and linker
+options to access that library.
+
+Using netpbm-config, it's possible to have a viable Netpbm installation where
+netpbm.config is the only file in any default search path.
+
+The xxx-config concept (in general, not just Netpbm) has largely been replaced
+by the pkg-config concept (see netpbm.pkgconfig above).
diff --git a/buildtools/configure.pl b/buildtools/configure.pl
index b03630d8..d4e9bee3 100755
--- a/buildtools/configure.pl
+++ b/buildtools/configure.pl
@@ -353,7 +353,7 @@ sub testCompile($$$) {
 sub testCompileLink($$$) {
     my ($flags, $cSourceCodeR, $successR) = @_;
 #-----------------------------------------------------------------------------
-#  Do a test compile of the program in @{$cSourceCodeR}.
+#  Do a test compile and link of the program in @{$cSourceCodeR}.
 #  
 #  Return $$successR == $TRUE iff the compile succeeds (exit code 0).
 #-----------------------------------------------------------------------------
@@ -1629,6 +1629,56 @@ sub testJpegHdr($) {
 
 
 
+sub warnJpegNotInDefaultPath($) {
+    my ($jpegLib) = @_;
+
+    print("You said your JPEG library is '$jpegLib', which says it is "
+          . "in the linker's default search path, but a test link we did "
+          . "failed as if it is not.  If it isn't, the build will fail\n");
+}
+
+
+
+sub testJpegLink($) {
+    my ($jpegLib) = @_;
+#-----------------------------------------------------------------------------
+#  See if we can link the JPEG library with the information user gave us.
+#  $jpegLib is the answer to the "what is your JPEG library" prompt, so
+#  it is either a library file name such as "libjpeg.so", in the linker's
+#  default search path, or it is an absolute path name such as
+#  "/usr/jpeg/lib/libjpeg.so".
+#
+#  We actually test only the default search path case, since users often
+#  incorrectly specify that by taking the default.
+#-----------------------------------------------------------------------------
+    if ($jpegLib =~ m{( lib | cyg ) (.+) \. ( so | a )$}x) {
+        my $libName = $2;
+
+        # It's like "libjpeg.so", so is a library in the default search path.
+        # $libName is "jpeg" in this example.
+
+        # First we test our test tool.  We can do this only with GCC.
+
+        my @emptySource;
+
+        testCompileLink('-nostartfiles', \@emptySource, \my $controlWorked);
+
+        if ($controlWorked) {
+            # The "control" case worked.  Now see if it still works when we add
+            # the JPEG library.
+
+            testCompileLink("-nostartfiles -l$libName", \@emptySource,
+                            \my $workedWithJpeg);
+
+            if (!$workedWithJpeg) {
+                warnJpegNotInDefaultPath($jpegLib);
+            }
+        }
+    }
+}
+
+
+
 sub testCompileZlibH($$) {
     my ($cflags, $successR) = @_;
 #-----------------------------------------------------------------------------
@@ -1895,6 +1945,8 @@ sub testConfiguration($$$$$$) {
 
     if (defined($jpeglib)) {
         testJpegHdr($jpeghdr_dir);
+
+        testJpegLink($jpeglib);
     }
     if (defined($pnglib) && defined($zlib)) {
         testPngHdr($pnghdr_dir, $zhdr_dir);
@@ -2289,7 +2341,7 @@ if ($platform eq "GNU") {
     # only Ppmtompeg and it isn't clear that using long instead of int is
     # ever right anyway.
 
-    push(@config_mk, "OMIT_NETWORK = y\n");
+    push(@config_mk, "OMIT_NETWORK = Y\n");
     push(@config_mk, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n");
 } elsif ($platform eq "IRIX") {
 #    push(@config_mk, "INSTALL = install\n");
@@ -2303,7 +2355,7 @@ if ($platform eq "GNU") {
         makeCompilerGcc(\@config_mk);
     }
     push(@config_mk, "EXE = .exe\n");
-    push(@config_mk, "OMIT_NETWORK = y\n");
+    push(@config_mk, "OMIT_NETWORK = Y\n");
 #    # Though it may not have the link as "ginstall", "install" in a Windows
 #    # Unix environment is usually GNU install.
 #    my $ginstall_result = `ginstall --version 2>/dev/null`;
diff --git a/buildtools/debian/README b/buildtools/debian/README
new file mode 100644
index 00000000..1c600c6b
--- /dev/null
+++ b/buildtools/debian/README
@@ -0,0 +1,122 @@
+Files in this directory are for creating a Debian package (.deb file),
+which one can use to install Netpbm on a Debian system (or a system running
+a derivative of Debian, such as Ubuntu or Mint).
+
+You can of course install on one of these systems by running 'installnetpbm',
+or otherwise inserting all the files in the proper place in your system file
+tree, but having a Debian package allows you to manage those files using
+Debian's normal package management.  The package management system will know
+where the Netpbm files came from, and you can upgrade or remove Netpbm easily.
+The package management system will also be aware of prerequisites of Netpbm
+and ensure that you don't have Debian's own inferior version of Netpbm
+installed (which would cause conflicts).
+
+The package we create is named 'netpbm-sf' (where the "sf" is from
+"SourceForge"), to be distinct from the package named "netpbm" which is part
+of Debian.
+
+To install Netpbm as a Debian package:
+
+  1) Follow the regular instructions to build and package Netpbm
+     (configure, make, make package).
+
+  2) With the root of the built Netpbm build tree as your current
+     directory:
+
+     $ make deb
+
+     (This defaults to getting Netpbm from /tmp/netpbm, which is also where
+     'make package' defaults to putting it).
+
+  3) $ dpkg --install netpbm-sfXXXX.deb
+
+     (netpbm-sfXXXX.deb is the file created by 'makedeb', in the current
+     directory).
+
+
+PREREQUSISITES
+--------------
+
+The following information was taken from the Wheezy version of Debian, in
+January 2014.
+
+You don't actually need the current version of any of these.  For example,
+while we list package libjpeg8-dev, the package libjpeg62-dev works fine.
+
+
+Building
+--------
+
+You need the following Debian packages to build all of Netpbm.
+
+You could omit some of these and, in the Netpbm build configuration dialog,
+indicate you don't have them, and the build will simply omit some parts.
+For example, if you don't install libx11-dev, the Netpbm build process
+will not build the 'pamx' program.
+
+  libjpeg8-dev
+  libpng12-0-dev
+  libsvga1-dev
+  libtiff5-dev
+  libx11-dev
+  libxml2a-dev
+  zlib1g-dev
+
+
+In addition, you need the following build tools:
+
+  make
+  gcc
+  flex
+  perl
+  pkg-config
+
+
+
+Running
+-------
+
+The following Debian packages are the known prerequisites for running Netpbm
+(and the package created by 'mkdeb' knows this).
+
+    libc6
+    libjpeg8
+    libpng12-0
+    libsvga1
+    libtiff5
+    libx11-6
+    zlib1g
+    ghostscript
+    perl
+    perl-base
+    bash
+
+Note that many of these are needed only for a few parts of Netpbm, and it will
+be pretty obvious what the problem is when you need the prerequisite package
+and don't have it, so if you don't want to install a prerequisite, it would
+probably be fine to force install Netpbm, ignoring the prerequisites.
+
+
+CONFLICTS WITH DEBIAN'S NETPBM
+------------------------------
+
+Debian has several packages of Netpbm, all based on a slightly modified
+Sourceforge Netpbm 9.25 from 2002 (don't be confused by Debian's numbering
+system, which makes it look like it is Netpbm 10).  If you want to install
+Sourceforge Netpbm on your system, you will want first to remove any of these
+you have installed:
+
+  netpbm
+  netpbm-dev
+  libnetpbm9
+  libnetpbm10
+
+Sourceforge Netpbm should be backward compatible with all of those.  Note that
+'mkdeb' makes only one package.  It contains the programs, the runtime
+libraries, and the development files.
+
+We have not yet worked out what has to be done about the fact that the Debian
+packaging system thinks the Debian Netpbm packages are prerequisites for
+things.  If you install Sourceforge Netpbm via the package created by mkdeb,
+you should tell the Netpbm maintainer whatever you learn about that.
+
diff --git a/buildtools/debian/mkdeb b/buildtools/debian/mkdeb
new file mode 100755
index 00000000..9c7b1735
--- /dev/null
+++ b/buildtools/debian/mkdeb
@@ -0,0 +1,541 @@
+#!/usr/bin/perl
+###############################################################################
+#                                mkdeb
+###############################################################################
+#
+#  This generates a Debian packge file (.deb) to install Sourceforge
+#  Netpbm on a Debian system.
+#
+#  This is especially useful because Debian does not have a good Debian
+#  package (what Debian contains is derived from Sourceforge Netpbm ca.
+#  2002).
+#
+###############################################################################
+
+use strict;
+use warnings;
+use English;
+use Getopt::Long;
+
+my $TRUE=1; my $FALSE = 0;
+
+
+
+sub parseCommandLine(@) {
+
+    local @ARGV = @_;  # GetOptions takes input from @ARGV only
+
+    my %cmdline;
+
+    my $validOptions = GetOptions(\%cmdline,
+                                  "buildtools=s",
+                                  "arch=s",
+                                  "pkgdir=s",
+        );
+
+    if (!$validOptions) {
+        print(STDERR "Invalid option syntax.\n");
+        exit(1);
+    }
+    if (@ARGV > 0) {
+        print(STDERR "This program takes no non-option arguments.  " .
+              "You specified ",
+              scalar(@ARGV), "\n");
+        exit(1);
+    } 
+
+    return(\%cmdline);
+}
+
+
+
+sub writeFile($$$$) {
+    my ($fileLinesR, $fileName, $executable, $errorR) = @_;
+
+    my $success = open(FILE, ">$fileName");
+    if ($success) {
+        if ($executable eq 'EXECUTABLE') {
+            chmod(0755, $fileName);
+        } else {
+            chmod(0644, $fileName);
+        }
+        foreach (@{$fileLinesR}) { print FILE; }
+        close(FILE);
+    } else {
+        $$errorR = "Unable to open the file " .
+            "'$fileName' for writing.  Errno=$ERRNO\n";
+    }
+}
+
+
+
+sub netpbmVersion($) {
+    my ($pkgdir) = @_;
+
+    my $versionFileName = "$pkgdir/VERSION";
+
+    my $versionOpened = open(VERSION, "<$versionFileName");
+
+    my $retval;
+    my $error;
+
+    if (!$versionOpened) {
+        $error = "Unable to open '$versionFileName' for reading.  " .
+            "Errno=$ERRNO\n";
+    } else {
+        my $version = <VERSION>;
+        chomp($version);
+
+        if ($version =~ m{^Netpbm (\S*)}) {
+            my ($versionNumber) = ($1);
+            $retval = $versionNumber;
+        } else {
+            die("Can't understand format of '$versionFileName': '$version'");
+        }
+        close(VERSION);
+    }
+
+    if ($error) {
+        print("Failed to determine the version of Netpbm from the package, "
+              . "so that will not be correct in netpbm.config and netpbm.pc.  "
+              . $error . "\n");
+        $retval = "???";
+    }
+    return $retval;
+}
+
+
+
+sub control($$) {
+    my ($release, $architecture) = @_;
+
+# The Debian packaging system doesn't provide a way to express Netpbm's actual
+# prerequisites.  For example, Netpbm needs Version 6.2 or better of Libjpeg,
+# but there is no way to state that here.  Instead, we state Libjpeg 8.
+# This makes the Netpbm package less useful.
+
+    my %control;
+
+    my $debianNativeNetpbm = 
+        'netpbm, ' .
+        'libnetpbm10, ' .
+        'libnetpbm10-dev, ' .
+        'netpbm-dev, ' .
+        'netpbm-nonfree, ' .
+        'pbmwbmp, ' .
+        'pnmtopng, ' .
+        'ucbmpeg';
+
+    $control{'Package'} = 'netpbm-sf';
+    $control{'Version'} = $release;
+    $control{'Architecture'} = $architecture;
+    $control{'Maintainer'} = 'Bryan Henderson <bryanh@giraffe-data.com>';
+    $control{'Installed-Size'} = '6164';
+    $control{'Depends'} =
+        'libc6, ' .
+        'libjpeg8, ' .
+        'libpng12-0, ' .
+        'libsvga1, ' .
+        'libtiff5, ' .
+        'libx11-6, ' .
+        'libxml2a, ' .
+        'zlib1g, ' .
+        'ghostscript, ' .
+        'perl, ' .
+        'perl-base, ' .
+        'bash'
+        ;
+    $control{'Recommends'} = '';
+    $control{'Recommends'} = '';
+    $control{'Conflicts'} = $debianNativeNetpbm;
+    $control{'Replaces'} = $debianNativeNetpbm;
+    $control{'Provides'} = 
+        'netpbm, ' .
+        'pbmwbmp, ' .
+        'pnmtopng, ' .
+        'netpbm-dev, ' .
+        'libnetpbm10'
+        ;
+    $control{'Section'} = 'graphics';
+    $control{'Priority'} = 'optional';
+    $control{'Section'} = 'graphics';
+    $control{'Description'} = 'Graphics conversion tools between image formats
+ Netpbm is a toolkit for manipulation of graphic images, including
+ conversion of images between a variety of different formats. There
+ are over 300 separate tools in the package including converters for
+ more than 80 graphics formats.  This is the Super Stable version from
+ the Sourceforge Netpbm project, unmodified.';
+
+    return \%control;
+}
+
+
+
+sub writeControlFile($$) {
+    my ($controlR, $fileName) = @_;
+
+    open(CTL, '>', $fileName)
+        or die "Can't open '$fileName': $ERRNO";
+
+    while (my ($key, $value) = each %{$controlR}) {
+        print CTL ("$key: $value\n");
+    }
+    
+    close(CTL);
+}
+
+
+
+sub createScripts($$) {
+    my ($dpkgDirName, $buildToolsDir) = @_;
+
+    my @scriptList = ('postinst', 'postrm');
+
+    my @scriptFileList = map("$buildToolsDir/debian/$_", @scriptList);
+
+    system('cp', @scriptFileList, "$dpkgDirName/DEBIAN/") &&
+        die("Failed to copy postinst, etc. to '$dpkgDirName/DEBIAN'.");
+
+    my @createdFileList = map("$dpkgDirName/DEBIAN/$_", @scriptList);
+
+    chmod(0755, @createdFileList);
+}
+
+
+
+sub createDirOrDie($) {
+    my ($newDirName) = @_;
+
+    mkdir($newDirName)
+        or die("Couldn't create directory '$newDirName'.  $ERRNO");
+
+    chmod(0755, $newDirName);
+}
+
+
+
+sub 
+processTemplate($$$) {
+    my ($templateR, $infoR, $outputR) = @_;
+
+    my @output;
+
+    foreach (@{$templateR}) {
+        if (m{^@}) {
+            # Comment -- ignore it.
+        } else {
+            if (defined($infoR->{VERSION})) {
+                s/\@VERSION\@/$infoR->{VERSION}/;
+            }
+            if (defined($infoR->{BINDIR})) {
+                s/\@BINDIR@/$infoR->{BINDIR}/;
+            }
+            if (defined($infoR->{LIBDIR})) {
+                s/\@LIBDIR@/$infoR-.{LIBDIR}/;
+            }
+            if (defined($infoR->{LINKDIR})) {
+                s/\@LINKDIR@/$infoR->{LINKDIR}/;
+            }
+            if (defined($infoR->{DATADIR})) {
+                s/\@DATADIR@/$infoR->{DATADIR}/;
+            }
+            if (defined($infoR->{INCLUDEDIR})) {
+                s/\@INCLUDEDIR@/$infoR->{INCLUDEDIR}/;
+            }
+            if (defined($infoR->{MANDIR})) {
+                s/\@MANDIR@/$infoR->{MANDIR}/;
+            }
+            push(@output, $_);
+        }
+    }
+    $$outputR = \@output;
+}
+
+
+
+
+
+sub makeConfig($$$) {
+    my ($fileName, $templateSubsR, $netpbmPkgDir) = @_;
+#-----------------------------------------------------------------------------
+# Install 'netpbm-config' -- a program you run to tell you things about
+# how Netpbm is installed.
+#-----------------------------------------------------------------------------
+    my $error;
+
+    my $configTemplateFilename = $netpbmPkgDir . "/config_template";
+
+    my $templateOpened = open(TEMPLATE, "<$configTemplateFilename");
+    if (!$templateOpened) {
+        $error = "Can't open template file '$configTemplateFilename'.\n";
+    } else {
+        my @template = <TEMPLATE>;
+
+        close(TEMPLATE);
+
+        processTemplate(\@template, $templateSubsR, \my $fileContentsR);
+
+        writeFile($fileContentsR, $fileName, 'EXECUTABLE', \$error);
+    }
+    if ($error) {
+        print(STDERR "Failed to create the Netpbm configuration program.  " .
+              "$error\n");
+    }
+}
+
+
+
+sub makePkgConfig($$$) {
+    my ($fileName, $templateSubsR, $netpbmPkgDir) = @_;
+#-----------------------------------------------------------------------------
+# Install a pkg-config file (netpbm.pc) - used by the 'pkg-config' program to
+# find out various things about how Netpbm is installed.
+#-----------------------------------------------------------------------------
+    my $error;
+
+    my $pcTemplateFilename = "$netpbmPkgDir/pkgconfig_template";
+
+    my $templateOpened = open(TEMPLATE, "<$pcTemplateFilename");
+    if (!$templateOpened) {
+        $error = "Can't open template file '$pcTemplateFilename'.\n";
+    } else {
+        my @template = <TEMPLATE>;
+
+        close(TEMPLATE);
+
+        processTemplate(\@template, $templateSubsR,
+                        \my $fileContentsR);
+
+        writeFile($fileContentsR, $fileName, 'NOTEXECUTABLE', \$error);
+    }
+    if ($error) {
+        print(STDERR "Failed to create the Netpbm Pkg-config file.  " .
+              "$error\n");
+    }
+}
+
+
+
+sub makeManweb($$) {
+    my ($dpkgDirName, $netpbmPkgDir) = @_;
+#-----------------------------------------------------------------------------
+#  Set up things so one can read the manual with e.g.
+#
+#    $ manweb pnmtojpeg
+#-----------------------------------------------------------------------------
+    my @manwebConfContents;
+
+    push(@manwebConfContents, "#Configuration file for Manweb\n");
+    push(@manwebConfContents, "webdir=/usr/man/web\n");
+
+    createDirOrDie("$dpkgDirName/etc");
+
+    my $manwebConfFileName = "$dpkgDirName/etc/manweb.conf";
+
+    writeFile(\@manwebConfContents, $manwebConfFileName,
+        'NOTEXECUTABLE', \my $error);
+
+    if ($error) {
+        die("Failed to create Manweb configuration file $manwebConfFileName");
+    }
+    createDirOrDie("$dpkgDirName/usr/man");
+
+    system('cp', '--archive',
+           "$netpbmPkgDir/man/web", "$dpkgDirName/usr/man/web") &&
+               die("Failed to copy executables from '$netpbmPkgDir/bin' " .
+                   "to '$dpkgDirName/usr/bin'");
+}
+
+
+
+sub buildDpkgBuildTree($$$$$) {
+    my ($dpkgDirName, $release, $netpbmPkgDir, $architecture,
+        $buildToolsDir) = @_;
+#-----------------------------------------------------------------------------
+#  Create the directory tree that is the input to Dpkg-deb --build.
+#  This tree contains all the files to be installed, _plus_ the control
+#  subdirectory named DEBIAN.
+#-----------------------------------------------------------------------------
+    print("Creating file tree for input to dpkg-deb --build as " .
+          "'$dpkgDirName'\n");
+
+    createDirOrDie($dpkgDirName);
+
+    createDirOrDie("$dpkgDirName/DEBIAN");
+
+    my $controlR = control($release, $architecture);
+
+    writeControlFile($controlR, "$dpkgDirName/DEBIAN/control");
+
+    createScripts($dpkgDirName, $buildToolsDir);
+
+    createDirOrDie("$dpkgDirName/usr");
+
+    system('cp', '--archive',
+           "$netpbmPkgDir/bin", "$dpkgDirName/usr/bin") &&
+               die("Failed to copy executables from '$netpbmPkgDir/bin' " .
+                   "to '$dpkgDirName/usr/bin'");
+
+    # doc.url is inappropriate with the program installed into the global
+    # /usr/bin .
+    unlink("$dpkgDirName/usr/bin/doc.url");
+
+    system("cp", "--archive", "$netpbmPkgDir/include",
+           "$dpkgDirName/usr/include") &&
+               die("Failed to copy header files from " .
+                   "'$netpbmPkgDir/include' " .
+                   "to '$dpkgDirName/usr/include'");
+
+    system("cp", "--archive",
+           "$netpbmPkgDir/lib", "$dpkgDirName/usr/lib") &&
+               die("Failed to copy libraries from '$netpbmPkgDir/lib' " .
+                   "to '$dpkgDirName/usr/lib'");
+
+    my @linkFileList = glob("$netpbmPkgDir/link/*");
+
+    if (@linkFileList > 0) {
+        system("cp", "--archive",
+               @linkFileList, "$dpkgDirName/usr/lib/") &&
+                   die("Failed to copy libraries from '$netpbmPkgDir/link' " .
+                       "to '$dpkgDirName/usr/lib'");
+    }
+    createDirOrDie("$dpkgDirName/usr/share");
+
+    system("cp", "--archive",
+           "$netpbmPkgDir/misc", "$dpkgDirName/usr/share/netpbm") &&
+               die("Failed to copy files from '$netpbmPkgDir/misc' " .
+                   "to '$dpkgDirName/usr/share/netpbm'");
+
+    # We install Netpbm in the default search path, so most of the values
+    # 'netpbm-config' returns are null strings.
+    my $templateSubsR =
+    {VERSION    => $release,
+     BINDIR     => '',
+     LIBDIR     => '',
+     LINKDIR    => '',
+     DATADIR    => '/usr/share/netpbm',
+     INCLUDEDIR => '',
+     MANDIR     => ''};
+
+    makeConfig("$dpkgDirName/usr/bin/netpbm-config", $templateSubsR,
+               $netpbmPkgDir);
+
+    createDirOrDie("$dpkgDirName/usr/lib/pkgconfig");
+
+    makePkgConfig("$dpkgDirName/usr/lib/pkgconfig/netpbm.pc", $templateSubsR,
+                  $netpbmPkgDir);
+
+    makeManweb($dpkgDirName, $netpbmPkgDir);
+}
+
+
+
+sub debianArchOfThisMachine() {
+
+    # A lazy implementation that handles only the most common cases
+
+    my $retval;
+
+    my $arch = qx{'arch'};
+    chomp($arch);
+
+    if ($arch eq 'x86_64') {
+        $retval = 'amd64';
+    } elsif ($arch =~ m{i.86}) {
+        $retval = 'i386';
+    } else {
+        die("Can't determine the Debian architecture classification of this " .
+            "system.  You'll have to give a -arch option");
+    }
+    return $retval;
+}
+
+
+
+sub buildToolsDir($) {
+    my ($cmdlineR) = @_;
+
+    my $retval;
+
+    if (exists($cmdlineR->{'buildtools'})) {
+        $retval = $cmdlineR->{'buildtools'};
+    } else {
+        if (-f('./debian/mkdeb')) {
+            $retval = '.';
+        } else {
+            die("The current directory does not appear to be 'buildtools' " .
+                "subdirectory of a Netpbm source tree, so you will have " .
+                "to use the -buildtools option to identify it");
+        }
+    }
+    return $retval;
+}
+
+
+
+sub netpbmPkgDir($) {
+    my ($cmdlineR) = @_;
+
+    my $retval;
+
+    if (exists($cmdlineR->{'pkgdir'})) {
+        $retval = $cmdlineR->{'pkgdir'};
+    } else {
+        my $tmpdir = $ENV{TMPDIR} || "/tmp";
+
+        my $defaultPkgDir = "$tmpdir/netpbm";
+
+        if (-d($defaultPkgDir)) {
+            $retval = $defaultPkgDir;
+        } else {
+            die("No directory '$defaultPkgDir' exists.  " .
+                "You can specify the Netpbm package directory " .
+                "(what 'make package' created), with a -pkgdir option");
+        }
+    }
+    return $retval;
+}
+
+
+
+sub arch($) {
+    my ($cmdlineR) = @_;
+
+    my $retval;
+
+    if (exists($cmdlineR->{'arch'})) {
+        $retval = $cmdlineR->{'arch'};
+    } else {
+        $retval = debianArchOfThisMachine();
+    }
+
+    return $retval;
+}
+
+
+
+###############################################################################
+#                               MAIN PROGRAM
+###############################################################################
+
+my $cmdlineR = parseCommandLine(@ARGV);
+
+my $buildTools = buildToolsDir($cmdlineR);
+
+my $netpbmPkgDir = netpbmPkgDir($cmdlineR);
+
+my $arch = arch($cmdlineR);
+
+my $release = netpbmVersion($netpbmPkgDir);
+
+my $dpkgDirName = "/tmp/netpbm-sf-$release";
+ 
+buildDpkgBuildTree($dpkgDirName, $release, $netpbmPkgDir, $arch,
+                   $buildTools);
+
+my $debFileName = 'netpbm-sf-' . $release . '_' . $arch . '.deb';
+
+system('dpkg-deb', '--build', $dpkgDirName, $debFileName) &&
+    die("dpgk-deb --build with input '$dpkgDirName' failed.");
+
+system('rm', '--recursive', $dpkgDirName);
diff --git a/buildtools/debian/postinst b/buildtools/debian/postinst
new file mode 100755
index 00000000..60836d52
--- /dev/null
+++ b/buildtools/debian/postinst
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+if [ "$1" = "configure" ]; then
+  ldconfig
+fi
diff --git a/buildtools/debian/postrm b/buildtools/debian/postrm
new file mode 100755
index 00000000..5f0c15e7
--- /dev/null
+++ b/buildtools/debian/postrm
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+if [ "$1" = "remove" ]; then
+  ldconfig
+fi
diff --git a/common.mk b/common.mk
index c096bd6e..51fbc438 100644
--- a/common.mk
+++ b/common.mk
@@ -449,7 +449,17 @@ ifeq ($(SYMLINKEXE)x,x)
   SYMLINKEXE := $(SYMLINK)
 endif
 
-$(PKGDIR)/%:
+# An implicit rule for $(PKGDIR)/% does not work because it causes Make
+# sometimes to believe the directory it creates from this rule is an unneeded
+# intermediate file and try to delete it later.  So we explicitly list the
+# possible directories under $(PKGDIR):
+
+PKGMANSUBDIRS = man1 man3 man5 web
+
+PKGSUBDIRS = bin include include/netpbm lib link misc \
+  $(PKGMANSUBDIRS:%=$(PKGMANDIR)/%)
+
+$(PKGSUBDIRS:%=$(PKGDIR)/%):
 	$(SRCDIR)/buildtools/mkinstalldirs $@
 
 .PHONY: install.merge
@@ -493,11 +503,13 @@ MANUALS1 = $(BINARIES) $(SCRIPTS)
 
 PKGMANDIR = man
 
-install.man1: $(PKGDIR)/$(PKGMANDIR)/man1 $(MANUALS1:%=%_installman1)
+install.man1: $(MANUALS1:%=%_installman1)
+
+install.man3: $(MANUALS3:%=%_installman3)
 
-install.man3: $(PKGDIR)/$(PKGMANDIR)/man3 $(MANUALS3:%=%_installman3)
+install.man5: $(MANUALS5:%=%_installman5)
 
-install.man5: $(PKGDIR)/$(PKGMANDIR)/man5 $(MANUALS5:%=%_installman5)
+install.manweb: $(MANUALS1:%=%_installmanweb) $(SUBDIRS:%=%/install.manweb)
 
 %_installman1: $(PKGDIR)/$(PKGMANDIR)/man1
 	perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman1=%) \
@@ -511,6 +523,10 @@ install.man5: $(PKGDIR)/$(PKGMANDIR)/man5 $(MANUALS5:%=%_installman5)
 	perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman5=%) \
           $(NETPBM_DOCURL) $< 5 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN)
 
+%_installmanweb: $(PKGDIR)/$(PKGMANDIR)/web
+	echo $(NETPBM_DOCURL)$(@:%_installmanweb=%).html \
+	  >$</$(@:%_installmanweb=%).url
+
 .PHONY: clean
 
 ifneq ($(EXE)x,x)
@@ -556,6 +572,9 @@ endif
 %/install.man:
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
+%/install.manweb:
+	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 %/install.data:
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c
index 254b6710..4008dbb2 100644
--- a/converter/other/cameratopam/camera.c
+++ b/converter/other/cameratopam/camera.c
@@ -687,15 +687,22 @@ static int  radc_token (int tree)
 void
 kodak_radc_load_raw()
 {
-    int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val;
+    int row, col, tree, nreps, rep, step, c, s, r, x, y, val;
+    unsigned int i;
     short last[3] = { 16,16,16 }, mul[3], buf[3][3][386];
 
     init_decoder();
     getbits(ifp, -1);
-    for (i=0; i < sizeof(buf)/sizeof(short); i++)
-        buf[0][0][i] = 2048;
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < 3; ++j) {
+            unsigned int k;
+            buf[i][j][k] = 2048;
+        }
+    }
     for (row=0; row < height; row+=4) {
-        for (i=0; i < 3; i++)
+        unsigned int i;
+        for (i = 0; i < 3; ++i)
             mul[i] = getbits(ifp, 6);
         FORC3 {
             val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c];
diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c
index 78e40baf..aa42da36 100644
--- a/converter/other/cameratopam/foveon.c
+++ b/converter/other/cameratopam/foveon.c
@@ -474,9 +474,13 @@ foveon_interpolate(float coeff[3][4]) {
 
     black = calloc (height, sizeof *black);
     for (row=0; row < height; row++) {
-        for (i=0; i < 6; i++)
-            ddft[0][0][i] = ddft[1][0][i] +
-                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        unsigned int i;
+        for (i=0; i < 3; ++i) {
+            unsigned int j;
+            for (j = 0; j < 2; ++j)
+                ddft[0][i][j] = ddft[1][i][j] +
+                    row / (height-1.0) * (ddft[2][i][j] - ddft[1][i][j]);
+        }
         FORC3 black[row][c] =
             ( foveon_avg (image[row*width]+c, dscr[0], cfilt) +
               foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3
@@ -522,9 +526,13 @@ foveon_interpolate(float coeff[3][4]) {
         FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0);
 
     for (row=0; row < height; row++) {
-        for (i=0; i < 6; i++)
-            ddft[0][0][i] = ddft[1][0][i] +
-                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        unsigned int i;
+        for (i = 0; i < 3; ++i) {
+            unsigned int j;
+            for (j = 0; j < 2; ++j)
+                ddft[0][i][j] = ddft[1][i][j] +
+                    row / (height-1.0) * (ddft[2][i][j] - ddft[1][i][j]);
+        }
         pix = (short*)image[row*width];
         memcpy (prev, pix, sizeof prev);
         frow = row / (height-1.0) * (dim[2]-1);
diff --git a/converter/other/pnmtopng.README b/converter/other/pnmtopng.README
deleted file mode 100644
index bfa524dc..00000000
--- a/converter/other/pnmtopng.README
+++ /dev/null
@@ -1,101 +0,0 @@
-Pnmtopng and Pngtopnm are based on programs of the same name in the 
-Pnmtopng package owned by Alexander Lehmann and Willem Van Schaik,
-available at http://www.libpng.org/pub/png/src on 2001.07.14.
-
-I added it to and adapted it to Netpbm on 2000.03.02 to make it more
-easily available to people.  I applied a patch on 2000.06.03 to bring
-it up the 2.37.4 release of that package.  I updated it again on
-2001.07.14 to bring it up to Release 2.37.5.  There is no process in
-place to bring improvements to the base package into the Netpbm
-version, but there hasn't been a lot of update activity anyway.
-
-Attached below is the file README from Release 2.37.5 of the base
-package.
-
-Here are the differences between the base and the Netpbm version:
-
-  I added an "unsigned" to make formal and actual arguments to png_sig_cmp()
-  match and quiet a compiler warning.
-
-  I fixed an "include" statement so the dependencies work out right.
-
-  I removed the BIGGRAYS stuff, which became obsolete in Netpbm 9.0
-
-  I replaced a PPM_MAXVAL with PPM_OVERALLMAXAL to handle the new 16 bits
-  formats.
-
-  macro VERSION is defined directly in pngtopnm.c and pnmtopng.c instead
-  of being included via file version.h.
-
-  Pnmtopng, since June 2001, reads one row at a time instead of holding 
-  the entire image in memory.  That makes it work with large bitmaps
-  where it would otherwise run out of memory.  It also works faster with
-  bitmaps since a bit takes up only a bit of memory in a cached input 
-  file, but 96 bits of memory after reading it into a Netpbm data 
-  structure.
-
-  The base Pnmtopng ignores -transparent if it specifies a color that
-  isn't in the image.  Netpbm's Pnmtopng selects a nearby color that _is_
-  in the image, which is what base Pnmtopng did before October 2000.
-  Netpbm's Pnmtopng lets you put an '=' sign before the color to specify
-  that you don't want a nearby color to be chosen, i.e. you want the
-  base Pnmtopng function.  This is consistent with Pnmtogif.
-
-There were some other changes necessary before Netpbm 9.0, but the change
-of the xelval type from 1 byte to 4 made them unnecessary.
-
-
-** PNMTOPNG / PNGTOPNM
-** version 2.37.5 - 24 October 2000
-
-[This is a semi-official bug-fix and enhancement release; I sort of took over
- maintenance of this package while Willem was on an extended bike trip, and
- for now I'm continuing with periodic, small updates.  Version 2.37 (March
- 1998) was never publicly released, partly because Willem had hoped to quiet
- gcc's "<var> might be clobbered by `longjmp'" warnings.  Those are fixed in
- 2.37.2; under Solaris, they resulted in stack corruption even when there was
- no error in the image files or libraries.  Version 2.37.3 fixes a minor bug
- w.r.t. error exits and generally does cleaner error exits (close files, etc.)
- Version 2.37.4 fixes a bug that caused 16-shade grayscale images to be written
- as 8-bit grayscale instead of (smaller) 4-bit colormapped images (bug report,
- analysis and fix by Rafal Rzeczkowski), and it supports the new/upcoming
- pbmplus release.  Version 2.37.5 fixes a bug in -transparent handling (pnmtopng
- no longer chooses an approximate color if the specified one isn't present) and
- quiets a gcc warning in the non-16-bit version.
- --Greg Roelofs]
-
-The utilities pnmtopng and pngtopnm are based on other pbm tools and require
-the libraries included in the pbmplus/netpbm package. Also required are the
-png library and the zlib compression library.
-
-These can be found at:
-	ftp://swrinde.nde.swri.edu/pub/png/src/libpng-*
-	ftp://swrinde.nde.swri.edu/pub/png/src/zlib-*
-	ftp://ftp.x.org/contrib/utilities/netpbm-1mar1994*
-or see
-	http://www.libpng.org/pub/png/apps/pnmtopng.html
-	http://netpbm.sourceforge.net/
-	http://www.acme.com/software/pbmplus/		[update coming soon?]
-
-To compile and install a makefile is provided. Do check the directories
-where you have put the required libraries. Then either accommodate the 
-makefile or make links from generic names (e.g., zlib) to version-specific
-directories (e.g., zlib-1.1.3), which is the recommended way.
-
-For testing purposes, have a look at the test-set PngSuite.tar.gz, which
-contains a small test-image for every PNG color type and for most PNG chunk
-types. It can be found at:
-	http://www.schaik.com/pngsuite/pngsuite.html
-	ftp://swrinde.nde.swri.edu/pub/png/images/suite/
-
-Other web pages with PNG images are at:
-	http://www.libpng.org/pub/png/png-textures.html
-	http://www.libpng.org/pub/png/pngs-img.html
-	http://www.libpng.org/pub/png/pngpic2.html
-	http://www.libpng.org/pub/png/colorcube/
-	http://www.libpng.org/pub/png/pngmisc.html#images
-
-------
-Alexander Lehmann <lehmann@usa.net>
-Willem van Schaik <willem@schaik.com>
-Greg Roelofs <newt@pobox.com>
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
index d867b37b..27e98611 100644
--- a/converter/other/pstopnm.c
+++ b/converter/other/pstopnm.c
@@ -33,6 +33,8 @@
 #include "shhopt.h"
 #include "nstring.h"
 
+static bool verbose;
+
 enum Orientation {PORTRAIT, LANDSCAPE, UNSPECIFIED};
 struct Box {
     /* Description of a rectangle within an image; all coordinates 
@@ -46,6 +48,19 @@ struct Box {
     int ury;  /* upper right Y coord */
 };
 
+struct Dimensions {
+/*----------------------------------------------------------------------------
+  Horizontal and vertical dimensions of something, both in pixels and
+  spatial distance (points).
+
+  Sizes are in pixels.  Resolutions are in dots per inch (pixels per inch);
+-----------------------------------------------------------------------------*/
+    unsigned int xsize;
+    unsigned int ysize;
+    unsigned int xres;
+    unsigned int yres;
+};
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -220,8 +235,7 @@ parseCommandLine(int argc, char ** argv,
 
 static void
 addPsToFileName(char          const origFileName[],
-                const char ** const newFileNameP,
-                bool          const verbose) {
+                const char ** const newFileNameP) {
 /*----------------------------------------------------------------------------
    If origFileName[] does not name an existing file, but the same
    name with ".ps" added to the end does, return the name with the .ps
@@ -256,30 +270,31 @@ addPsToFileName(char          const origFileName[],
 
 
 static void
-computeSizeResFromSizeSpec(unsigned int   const requestedXsize,
-                           unsigned int   const requestedYsize,
-                           unsigned int   const imageWidth,
-                           unsigned int   const imageHeight,
-                           unsigned int * const xsizeP,
-                           unsigned int * const ysizeP,
-                           unsigned int * const xresP,
-                           unsigned int * const yresP) {
+computeSizeResFromSizeSpec(unsigned int        const requestedXsize,
+                           unsigned int        const requestedYsize,
+                           unsigned int        const imageWidth,
+                           unsigned int        const imageHeight,
+                           struct Dimensions * const imageDimP) {
 
     if (requestedXsize) {
-        *xsizeP = requestedXsize;
-        *xresP = (unsigned int) (requestedXsize * 72 / imageWidth + 0.5);
+        imageDimP->xsize = requestedXsize;
+        imageDimP->xres = (unsigned int)
+            (requestedXsize * 72 / imageWidth + 0.5);
         if (!requestedYsize) {
-            *yresP = *xresP;
-            *ysizeP = (unsigned int) (imageHeight * (float)*yresP/72 + 0.5);
+            imageDimP->yres = imageDimP->xres;
+            imageDimP->ysize = (unsigned int)
+                (imageHeight * (float)imageDimP->yres/72 + 0.5);
             }
         }
 
     if (requestedYsize) {
-        *ysizeP = requestedYsize;
-        *yresP = (unsigned int) (requestedYsize * 72 / imageHeight + 0.5);
+        imageDimP->ysize = requestedYsize;
+        imageDimP->yres = (unsigned int)
+            (requestedYsize * 72 / imageHeight + 0.5);
         if (!requestedXsize) {
-            *xresP = *yresP;
-            *xsizeP = (unsigned int) (imageWidth * (float)*xresP/72 + 0.5);
+            imageDimP->xres = imageDimP->yres;
+            imageDimP->xsize = (unsigned int)
+                (imageWidth * (float)imageDimP->xres/72 + 0.5);
         }
     } 
 }
@@ -287,42 +302,36 @@ computeSizeResFromSizeSpec(unsigned int   const requestedXsize,
 
 
 static void
-computeSizeResBlind(unsigned int   const xmax,
-                    unsigned int   const ymax,
-                    unsigned int   const imageWidth,
-                    unsigned int   const imageHeight,
-                    bool           const nocrop,
-                    unsigned int * const xsizeP,
-                    unsigned int * const ysizeP,
-                    unsigned int * const xresP,
-                    unsigned int * const yresP) {
-
-    *xresP = *yresP = MIN(xmax * 72 / imageWidth, 
-                          ymax * 72 / imageHeight);
+computeSizeResBlind(unsigned int        const xmax,
+                    unsigned int        const ymax,
+                    unsigned int        const imageWidth,
+                    unsigned int        const imageHeight,
+                    bool                const nocrop,
+                    struct Dimensions * const imageDimP) {
+    
+    imageDimP->xres = imageDimP->yres = MIN(xmax * 72 / imageWidth, 
+                                            ymax * 72 / imageHeight);
     
     if (nocrop) {
-        *xsizeP = xmax;
-        *ysizeP = ymax;
+        imageDimP->xsize = xmax;
+        imageDimP->ysize = ymax;
     } else {
-        *xsizeP = (unsigned int) (imageWidth * (float)*xresP / 72 + 0.5);
-        *ysizeP = (unsigned int) (imageHeight * (float)*yresP / 72 + 0.5);
+        imageDimP->xsize = (unsigned int)
+            (imageWidth * (float)imageDimP->xres / 72 + 0.5);
+        imageDimP->ysize = (unsigned int)
+            (imageHeight * (float)imageDimP->yres / 72 + 0.5);
     }
 }
 
 
 
 static void
-computeSizeRes(struct CmdlineInfo const cmdline, 
-               enum Orientation   const orientation, 
-               struct Box         const borderedBox,
-               unsigned int *     const xsizeP, 
-               unsigned int *     const ysizeP,
-               unsigned int *     const xresP, 
-               unsigned int *     const yresP) {
+computeSizeRes(struct CmdlineInfo  const cmdline, 
+               struct Box          const borderedBox,
+               struct Dimensions * const imageDimP) {
 /*----------------------------------------------------------------------------
-  Figure out how big the output image should be (return as
-  *xsizeP and *ysizeP) and what output device resolution Ghostscript
-  should assume (return as *xresP, *yresP).
+  Figure out how big the output image should be and what output device
+  resolution Ghostscript should assume (return as *imageDimP).
 
   A resolution number is the number of pixels per inch that the a
   printer prints.  Since we're emulating a printed page with a PNM
@@ -337,38 +346,33 @@ computeSizeRes(struct CmdlineInfo const cmdline,
   tell Ghostscript that our horizontal output device resolution is 500
   pixels per inch.
   
-  *xresP and *yresP are in dots per inch.
+  X and Y in all returned values is with respect to the image, not the
+  page.  Note that the image might be placed sideways on the page, so that
+  page X and Y would be reversed from image X and Y.
 -----------------------------------------------------------------------------*/
-    unsigned int sx, sy;
-        /* The horizontal and vertical sizes of the input image, in points
-           (1/72 inch)
-        */
-
-    if (orientation == LANDSCAPE) {
-        sx = borderedBox.ury - borderedBox.lly;
-        sy = borderedBox.urx - borderedBox.llx;
-    } else {
-        sx = borderedBox.urx - borderedBox.llx;
-        sy = borderedBox.ury - borderedBox.lly;
-    }
+    /* The horizontal and vertical sizes of the input image, in points
+       (1/72 inch)
+    */
+    unsigned int const sx = borderedBox.urx - borderedBox.llx;
+    unsigned int const sy = borderedBox.ury - borderedBox.lly;
 
     if (cmdline.dpi) {
         /* User gave resolution; we figure out output image size */
-        *xresP = *yresP = cmdline.dpi;
-        *xsizeP = (int) (cmdline.dpi * sx / 72 + 0.5);
-        *ysizeP = (int) (cmdline.dpi * sy / 72 + 0.5);
+        imageDimP->xres = imageDimP->yres = cmdline.dpi;
+        imageDimP->xsize = ROUNDU(cmdline.dpi * sx / 72.0);
+        imageDimP->ysize = ROUNDU(cmdline.dpi * sy / 72.0);
     } else  if (cmdline.xsize || cmdline.ysize)
         computeSizeResFromSizeSpec(cmdline.xsize, cmdline.ysize, sx, sy,
-                                   xsizeP, ysizeP, xresP, yresP);
+                                   imageDimP);
     else 
         computeSizeResBlind(cmdline.xmax, cmdline.ymax, sx, sy, cmdline.nocrop,
-                            xsizeP, ysizeP, xresP, yresP);
+                            imageDimP);
 
     if (cmdline.verbose) {
         pm_message("output is %u pixels wide X %u pixels high",
-                   *xsizeP, *ysizeP);
+                   imageDimP->xsize, imageDimP->ysize);
         pm_message("output device resolution is %u dpi horiz, %u dpi vert",
-                   *xresP, *yresP);
+                   imageDimP->xres, imageDimP->yres);
     }
 }
 
@@ -377,8 +381,7 @@ computeSizeRes(struct CmdlineInfo const cmdline,
 enum PostscriptLanguage {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT};
 
 static enum PostscriptLanguage
-languageDeclaration(char const inputFileName[],
-                    bool const verbose) {
+languageDeclaration(char const inputFileName[]) {
 /*----------------------------------------------------------------------------
   Return the Postscript language in which the file declares it is written.
   (Except that if the file is on Standard Input or doesn't validly declare
@@ -421,8 +424,7 @@ languageDeclaration(char const inputFileName[],
 
 static struct Box
 computeBoxToExtract(struct Box const cmdlineExtractBox,
-                    char       const inputFileName[],
-                    bool       const verbose) {
+                    char       const inputFileName[]) {
 
     struct Box retval;
 
@@ -572,8 +574,7 @@ computeOrientation(struct CmdlineInfo const cmdline,
 static struct Box
 addBorders(struct Box const inputBox, 
            float      const xborderScale,
-           float      const yborderScale,
-           bool       const verbose) {
+           float      const yborderScale) {
 /*----------------------------------------------------------------------------
    Return a box which is 'inputBox' plus some borders.
 
@@ -592,16 +593,13 @@ addBorders(struct Box const inputBox,
     assert(inputBox.urx >= inputBox.llx);
     assert(inputBox.ury >= inputBox.lly);
 
-    assert(inputBox.llx >= leftRightBorderSize);
-    assert(inputBox.lly >= topBottomBorderSize);
-
-    retval.llx = inputBox.llx - leftRightBorderSize;
-    retval.lly = inputBox.lly - topBottomBorderSize;
-    retval.urx = inputBox.urx + leftRightBorderSize;
-    retval.ury = inputBox.ury + topBottomBorderSize;
+    retval.llx = inputBox.llx - (int)leftRightBorderSize;
+    retval.lly = inputBox.lly - (int)topBottomBorderSize;
+    retval.urx = inputBox.urx + (int)leftRightBorderSize;
+    retval.ury = inputBox.ury + (int)topBottomBorderSize;
 
     if (verbose)
-        pm_message("With borders, extracted box is ((%u,%u),(%u,%u))",
+        pm_message("With borders, extracted box is ((%d,%d),(%d,%d))",
                    retval.llx, retval.lly, retval.urx, retval.ury);
 
     return retval;
@@ -609,32 +607,45 @@ addBorders(struct Box const inputBox,
 
 
 
-static const char *
-computePstrans(struct Box       const box,
-               enum Orientation const orientation,
-               int              const xsize,
-               int              const ysize, 
-               int              const xres,
-               int              const yres) {
+static void
+writePstrans(struct Box        const box,
+             struct Dimensions const d,
+             enum Orientation  const orientation,
+             FILE *            const pipeToGsP) {
 
-    const char * retval;
+    int const xsize = d.xsize;
+    int const ysize = d.ysize;
+    int const xres  = d.xres;
+    int const yres  = d.yres;
 
-    if (orientation == PORTRAIT) {
+    const char * pstrans;
+
+    switch (orientation) {
+    case PORTRAIT: {
         int llx, lly;
         llx = box.llx - (xsize * 72 / xres - (box.urx - box.llx)) / 2;
         lly = box.lly - (ysize * 72 / yres - (box.ury - box.lly)) / 2;
-        pm_asprintf(&retval, "%d neg %d neg translate", llx, lly);
-    } else {
+        pm_asprintf(&pstrans, "%d neg %d neg translate", llx, lly);
+    } break;
+    case LANDSCAPE: {
         int llx, ury;
-        llx = box.llx - (ysize * 72 / yres - (box.urx - box.llx)) / 2;
-        ury = box.ury + (xsize * 72 / xres - (box.ury - box.lly)) / 2;
-        pm_asprintf(&retval, "90 rotate %d neg %d neg translate", llx, ury);
+        llx = box.llx - (xsize * 72 / xres - (box.urx - box.llx)) / 2;
+        ury = box.ury + (ysize * 72 / yres - (box.ury - box.lly)) / 2;
+        pm_asprintf(&pstrans, "90 rotate %d neg %d neg translate", llx, ury);
+    } break;
+    case UNSPECIFIED:
+        assert(false);
     }
 
-    if (retval == NULL)
+    if (pstrans == pm_strsol)
         pm_error("Unable to allocate memory for pstrans");
 
-    return retval;
+    if (verbose) 
+        pm_message("Postscript prefix command: '%s'", pstrans);
+
+    fprintf(pipeToGsP, "%s\n", pstrans);
+
+    pm_strfree(pstrans);
 }
 
 
@@ -753,16 +764,19 @@ findGhostscriptProg(const char ** const retvalP) {
 
 
 static void
-execGhostscript(int          const inputPipeFd,
-                char         const ghostscriptDevice[],
-                char         const outfileArg[], 
-                int          const xsize,
-                int          const ysize, 
-                int          const xres,
-                int          const yres,
-                unsigned int const textalphabits,
-                bool         const verbose) {
-    
+execGhostscript(int               const inputPipeFd,
+                char              const ghostscriptDevice[],
+                char              const outfileArg[], 
+                struct Dimensions const pageDim,
+                unsigned int      const textalphabits) {
+/*----------------------------------------------------------------------------
+   Exec the Ghostscript program and have it execute the Postscript program
+   that it receives on 'inputPipeFd', then exit.
+
+   'pageDim' describes the print area.  X and Y in 'pageDim' are with respect
+   to the page, independent of whether the program we receive on 'inputPipeFd'
+   puts an image in there sideways.
+-----------------------------------------------------------------------------*/
     const char * arg0;
     const char * ghostscriptProg;
     const char * deviceopt;
@@ -781,8 +795,8 @@ execGhostscript(int          const inputPipeFd,
     pm_asprintf(&arg0, "gs");
     pm_asprintf(&deviceopt, "-sDEVICE=%s", ghostscriptDevice);
     pm_asprintf(&outfileopt, "-sOutputFile=%s", outfileArg);
-    pm_asprintf(&gopt, "-g%dx%d", xsize, ysize);
-    pm_asprintf(&ropt, "-r%dx%d", xres, yres);
+    pm_asprintf(&gopt, "-g%dx%d", pageDim.xsize, pageDim.ysize);
+    pm_asprintf(&ropt, "-r%dx%d", pageDim.xres, pageDim.yres);
     pm_asprintf(&textalphabitsopt, "-dTextAlphaBits=%u", textalphabits);
 
     /* -dSAFER causes Postscript to disable %pipe and file operations,
@@ -810,23 +824,120 @@ execGhostscript(int          const inputPipeFd,
 
 
 static void
-executeGhostscript(char                    const pstrans[],
+feedPsToGhostScript(const char *            const inputFileName,
+                    struct Box              const borderedBox,
+                    struct Dimensions       const imageDim,
+                    enum Orientation        const orientation,
+                    int                     const pipeToGhostscriptFd,
+                    enum PostscriptLanguage const language) {
+/*----------------------------------------------------------------------------
+   Send a Postscript program to the Ghostscript process running on the
+   other end of the pipe 'pipeToGhostscriptFd'.  That program is mostly
+   the contents of file 'inputFileName' (special value "-" means Standard
+   Input), but we may add a little to it.
+
+   The image has dimensions 'imageDim' and is oriented on the page according
+   to 'orientation' ('imageDim' X and Y are with respect to the image itself,
+   without regard to how it is oriented on the page).
+-----------------------------------------------------------------------------*/
+    FILE * pipeToGsP;  /* Pipe to Ghostscript's standard input */
+    FILE * ifP;
+    bool eof;  /* End of file on input */
+
+    pipeToGsP = fdopen(pipeToGhostscriptFd, "w");
+    if (pipeToGsP == NULL) 
+        pm_error("Unable to open stream on pipe to Ghostscript process.");
+    
+    ifP = pm_openr(inputFileName);
+    /*
+      In encapsulated Postscript, we the encapsulator are supposed to
+      handle showing the page (which we do by passing a showpage
+      statement to Ghostscript).  Any showpage statement in the 
+      input must be defined to have no effect.
+          
+      See "Enscapsulated PostScript Format File Specification",
+      v. 3.0, 1 May 1992, in particular Example 2, p. 21.  I found
+      it at 
+      http://partners.adobe.com/asn/developer/pdfs/tn/5002.EPSF_Spec.pdf
+      The example given is a much fancier solution than we need
+      here, I think, so I boiled it down a bit.  JM 
+    */
+    if (language == ENCAPSULATED_POSTSCRIPT)
+        fprintf(pipeToGsP, "\n/b4_Inc_state save def /showpage { } def\n");
+ 
+    writePstrans(borderedBox, imageDim, orientation, pipeToGsP);
+
+    /* If our child dies, it closes the pipe and when we next write to it,
+       we get a SIGPIPE.  We must survive that signal in order to report
+       on the fate of the child.  So we ignore SIGPIPE:
+    */
+    signal(SIGPIPE, SIG_IGN);
+
+    eof = FALSE;
+    while (!eof) {
+        char buffer[4096];
+        size_t readCt;
+            
+        readCt = fread(buffer, 1, sizeof(buffer), ifP);
+        if (readCt == 0) 
+            eof = TRUE;
+        else 
+            fwrite(buffer, 1, readCt, pipeToGsP);
+    }
+    pm_close(ifP);
+
+    if (language == ENCAPSULATED_POSTSCRIPT)
+        fprintf(pipeToGsP, "\nb4_Inc_state restore showpage\n");
+
+    fclose(pipeToGsP);
+}        
+
+
+
+static struct Dimensions
+pageDimFromImageDim(struct Dimensions const imageDim,
+                    enum Orientation  const orientation) {
+/*----------------------------------------------------------------------------
+   The dimensions of the page of an image whose dimensions are
+   'imageDim', if we place it on the page with orientation 'orientation'.
+
+   (I.e. swap and X and Y if landscape orientation).
+
+   'orientation' must not be UNSPECIFIED.
+-----------------------------------------------------------------------------*/
+    struct Dimensions retval;
+
+    switch (orientation) {
+    case PORTRAIT:
+        retval = imageDim;
+        break;
+    case LANDSCAPE:
+        retval.xsize = imageDim.ysize;
+        retval.ysize = imageDim.xsize;
+        retval.xres  = imageDim.yres;
+        retval.yres  = imageDim.xres;
+        break;
+    case UNSPECIFIED:
+        assert(false);
+        break;
+    }
+
+    return retval;
+}
+
+
+
+static void
+executeGhostscript(char                    const inputFileName[],
+                   struct Box              const borderedBox,
+                   struct Dimensions       const imageDim,
+                   enum Orientation        const orientation,
                    char                    const ghostscriptDevice[],
                    char                    const outfileArg[], 
-                   int                     const xsize,
-                   int                     const ysize, 
-                   int                     const xres,
-                   int                     const yres,
                    unsigned int            const textalphabits,
-                   char                    const inputFileName[], 
-                   enum PostscriptLanguage const language,
-                   bool                    const verbose) {
+                   enum PostscriptLanguage const language) {
 
-    int gsTermStatus;  /* termination status of Ghostscript process */
-    FILE * pipeToGsP;  /* Pipe to Ghostscript's standard input */
-    FILE * ifP;
     int rc;
-    int eof;  /* End of file on input */
     int pipefd[2];
 
     if (strlen(outfileArg) > 80)
@@ -845,65 +956,23 @@ executeGhostscript(char                    const pstrans[],
         /* Child process */
         close(pipefd[1]);
         execGhostscript(pipefd[0], ghostscriptDevice, outfileArg,
-                        xsize, ysize, xres, yres, textalphabits,
-                        verbose);
+                        pageDimFromImageDim(imageDim, orientation),
+                        textalphabits);
     } else {
+        /* parent process */
         pid_t const ghostscriptPid = rc;
         int const pipeToGhostscriptFd = pipefd[1];
-        /* parent process */
-        close(pipefd[0]);
-
-        pipeToGsP = fdopen(pipeToGhostscriptFd, "w");
-        if (pipeToGsP == NULL) 
-            pm_error("Unable to open stream on pipe to Ghostscript process.");
-    
-        ifP = pm_openr(inputFileName);
-        /*
-          In encapsulated Postscript, we the encapsulator are supposed to
-          handle showing the page (which we do by passing a showpage
-          statement to Ghostscript).  Any showpage statement in the 
-          input must be defined to have no effect.
-          
-          See "Enscapsulated PostScript Format File Specification",
-          v. 3.0, 1 May 1992, in particular Example 2, p. 21.  I found
-          it at 
-          http://partners.adobe.com/asn/developer/pdfs/tn/5002.EPSF_Spec.pdf
-          The example given is a much fancier solution than we need
-          here, I think, so I boiled it down a bit.  JM 
-        */
-        if (language == ENCAPSULATED_POSTSCRIPT)
-            fprintf(pipeToGsP, "\n/b4_Inc_state save def /showpage { } def\n");
- 
-        if (verbose) 
-            pm_message("Postscript prefix command: '%s'", pstrans);
-
-        fprintf(pipeToGsP, "%s\n", pstrans);
 
-        /* If our child dies, it closes the pipe and when we next write to it,
-           we get a SIGPIPE.  We must survive that signal in order to report
-           on the fate of the child.  So we ignore SIGPIPE:
-        */
-        signal(SIGPIPE, SIG_IGN);
+        int gsTermStatus;  /* termination status of Ghostscript process */
+        pid_t rc;
 
-        eof = FALSE;
-        while (!eof) {
-            char buffer[4096];
-            int bytes_read;
-            
-            bytes_read = fread(buffer, 1, sizeof(buffer), ifP);
-            if (bytes_read == 0) 
-                eof = TRUE;
-            else 
-                fwrite(buffer, 1, bytes_read, pipeToGsP);
-        }
-        pm_close(ifP);
+        close(pipefd[0]);
 
-        if (language == ENCAPSULATED_POSTSCRIPT)
-            fprintf(pipeToGsP, "\nb4_Inc_state restore showpage\n");
+        feedPsToGhostScript(inputFileName, borderedBox,
+                            imageDim, orientation,
+                            pipeToGhostscriptFd, language);
 
-        fclose(pipeToGsP);
-        
-        waitpid(ghostscriptPid, &gsTermStatus, 0);
+        rc = waitpid(ghostscriptPid, &gsTermStatus, 0);
         if (rc < 0)
             pm_error("Wait for Ghostscript process to terminated failed.  "
                      "errno = %d (%s)", errno, strerror(errno));
@@ -930,8 +999,8 @@ main(int argc, char ** argv) {
     struct CmdlineInfo cmdline;
     const char * inputFileName;  /* malloc'ed */
         /* The file specification of our Postscript input file */
-    unsigned int xres, yres;    /* Resolution in pixels per inch */
-    unsigned int xsize, ysize;  /* output image size in pixels */
+    struct Dimensions imageDim;
+        /* Size and resolution of the input image */
     struct Box extractBox;
         /* coordinates of the box within the input we are to extract; i.e.
            that will become the output. 
@@ -943,30 +1012,25 @@ main(int argc, char ** argv) {
     enum Orientation orientation;
     const char * ghostscriptDevice;
     const char * outfileArg;
-    const char * pstrans;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    addPsToFileName(cmdline.inputFileName, &inputFileName, cmdline.verbose);
+    verbose = cmdline.verbose;
+
+    addPsToFileName(cmdline.inputFileName, &inputFileName);
 
-    extractBox = computeBoxToExtract(cmdline.extractBox, inputFileName, 
-                                      cmdline.verbose);
+    extractBox = computeBoxToExtract(cmdline.extractBox, inputFileName);
 
-    language = languageDeclaration(inputFileName, cmdline.verbose);
+    language = languageDeclaration(inputFileName);
     
     orientation = computeOrientation(cmdline, extractBox);
 
-    borderedBox = addBorders(extractBox, cmdline.xborder, cmdline.yborder,
-                             cmdline.verbose);
+    borderedBox = addBorders(extractBox, cmdline.xborder, cmdline.yborder);
 
-    computeSizeRes(cmdline, orientation, borderedBox, 
-                   &xsize, &ysize, &xres, &yres);
+    computeSizeRes(cmdline, borderedBox, &imageDim);
     
-    pstrans = computePstrans(borderedBox, orientation,
-                             xsize, ysize, xres, yres);
-
     outfileArg = computeOutfileArg(cmdline);
 
     ghostscriptDevice = 
@@ -974,14 +1038,12 @@ main(int argc, char ** argv) {
     
     pm_message("Writing %s format", ghostscriptDevice);
     
-    executeGhostscript(pstrans, ghostscriptDevice, outfileArg, 
-                       xsize, ysize, xres, yres, cmdline.textalphabits,
-                       inputFileName,
-                       language, cmdline.verbose);
+    executeGhostscript(inputFileName, borderedBox, imageDim, orientation,
+                       ghostscriptDevice, outfileArg, cmdline.textalphabits,
+                       language);
 
     pm_strfree(ghostscriptDevice);
     pm_strfree(outfileArg);
-    pm_strfree(pstrans);
     
     return 0;
 }
diff --git a/converter/ppm/ppmtompeg/Makefile b/converter/ppm/ppmtompeg/Makefile
index a1004fdd..4f244ae9 100644
--- a/converter/ppm/ppmtompeg/Makefile
+++ b/converter/ppm/ppmtompeg/Makefile
@@ -51,7 +51,7 @@ MP_ENCODE_OBJS = \
 MP_OTHER_OBJS = mpeg.o subsample.o param.o rgbtoycc.o \
 	readframe.o combine.o jrevdct.o frame.o fsize.o frametype.o \
 	specifics.o rate.o opts.o input.o
-ifeq ($(OMIT_NETWORK),y)
+ifeq ($(OMIT_NETWORK),Y)
   MP_OTHER_OBJS += noparallel.o
 else
   MP_OTHER_OBJS += parallel.o psocket.o
diff --git a/doc/HISTORY b/doc/HISTORY
index 9928f0a8..b5b9b43a 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,58 +4,83 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-14.03.02 BJH  Release 10.65.07
+14.03.30 BJH  Release 10.66.00
+
+              Add pamvalidate.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+
+              Add pamfix: Does what pamfixtrunc did, plus repairs excessive
+              sample values.
+
+              pamfixtrunc: implement as call to new pamfix.
+
+              pgmhist, ppmhist: Add -forensic: Analyze invalid >maxval pixels.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+
+              pgmramp: add -diagonal.  Thanks Prophet of the Way
+              <afu@wta.att.ne.jp>.
+
+              libnetpbm: Read functions validate that sample values do not
+              exceed maxval.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+
+              libnetpbm: Validate image dimensions are small enough that you
+              can allocate a row buffer.  Thanks Prophet of the Way
+              <afu@wta.att.ne.jp>.
 
               pgmhist: fix incorrect report of quantiles or crash due to array
               bounds violation in some builds.  Thanks Prophet of the Way
               <afu@wta.att.ne.jp>.  Always broken.  Quantile reporting was
               new in Netpbm 10.61 (December 2012).
 
-              Windows build: fix missing .exe on copies of programs under
-              their old names.
-
-14.03.01 BJH  Release 10.65.06
-
               pgmhist: fix buffer overrun with -median.  Always broken.
               -median was new in Netpbm 10.61 (December 2012).
 
-14.02.24 BJH  Release 10.65.05
+              pnmmargin: fix for size 0 and superfluous "unexpected operator" 
+              message with size != 0.  Introduced in 10.42.
 
-              pgmtexture: fix buffer overflow.  Always broken.  (Program
-              was added in primordial Netpbm in 1991).
+              pstopnm: fix wrong interpretations of -xsize and -ysize when
+              rendering image in landscape (rotated).  This can appear as
+              stretching and squashing.  Probably always broken.
 
-              Windows build: fix Ppmtompeg build failure in non-Cygwin build
-              due to missing sys/utsname.h.
+              pstopnm: fix wrong orientation sometimes when you specify
+              both -xsize and -ysize.  Introduced in 10.65.
 
-14.02.09 BJH  Release 10.65.04
+              pgmramp: fix bogus output with really large images.  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
 
               ppmrelief: fix out-of-bound values in output.  Always broken.
+              ppmrelief was new in primordial Netpbm in 1989.
               Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
               ppmrelief: fix crash when input image is too small.  Always
-              broken.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              broken.  ppmrelief was new in primordial Netpbm in 1989.  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
+
+              pgmtexture: fix buffer overflow.  Always broken.  (Program
+              was added in primordial Netpbm in 1991).
 
               pamdeinterlace: fix incorrect output with -takeodd and image has
               only one row.  Always broken (pamdeinterlace was introduced in
               Netpbm 9.21 (January 2001)).  Thanks Prophet of the Way
               <afu@wta.att.ne.jp>.
 
-14.01.06 BJH  Release 10.65.03
+              configure: warn if user says JPEG library is in the linker's
+              default search path, but it isn't.
+
+              build/install: add tools for creating a Debian package.
 
               make package: Include template for pkgconfig file.
 
+              make package: Include a man/web directory with .url files for
+              each manual page.
+
               test: Add -portrait to invocations of pstopnm in order to get
               proper round trips.
 
-14.01.01 BJH  Release 10.65.02
-
-              pnmmargin: fix for size 0 and superfluous "unexpected operator" 
-              message with size != 0.  Introduced in 10.42.
-
-13.12.28 BJH  Release 10.65.01
+              Windows build: fix Ppmtompeg build failure in non-Cygwin build
+              due to missing sys/utsname.h.
 
-              pstopnm: fix wrong orientation sometimes when you specify
-              both -xsize and -ysize.  Introduced in 10.65.00.
+              Windows build: fix missing .exe on copies of programs under
+              their old names.
 
 13.12.26 BJH  Release 10.65.00
 
@@ -228,7 +253,7 @@ CHANGE HISTORY
               build: fix problem with creating lib/util that already exists.
               Broken in Netpbm 10.62.
 
-12.03.28 BJH  Release 10.62.00
+13.03.28 BJH  Release 10.62.00
 
               pnmtorast: set don't care bytes to zero to make output
               repeatable.
@@ -247,7 +272,7 @@ CHANGE HISTORY
               pamstereogram: fix bug: garbage in -verbose listing.  Broken
               since Netpbm 10.61
 
-              MinGW build: various fixes.
+              Windows MinGW build: various fixes.
 
 12.12.30 BJH  Release 10.61.00
 
@@ -1201,6 +1226,8 @@ CHANGE HISTORY
               too large for computation.  Thanks Prophet of the Way
               <afu@wta.att.ne.jp>.
 
+              pnmmargin: don't crash with zero margin request.
+
               pnmtile: deal with zero width/height.
 
               pbmtext: fix negative -space.
diff --git a/doc/INSTALL b/doc/INSTALL
index f8397ca2..33bb4bc7 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -20,10 +20,16 @@ finally
 
 to install it into your system.  
 
-If you're building Latest Netpbm (as opposed to Stable Netpbm), there
-are probably known bugs.  Some may even prevent Netpbm from building.
-These are listed in the release notes for the release on Sourceforge.
-Check it out.
+If you have a Debian-like system, that uses Dpkg for package management,
+it's better to create a Debian package file and install it as follows in
+place of the 'installnetpbm' step above.
+
+    make deb
+    dpkg --install netpbm-sf-*.deb
+
+More information on building and installing on Debian are in the file
+buildtools/debian/README in the source tree.
+
 
 The 'configure' program is not GNU Autoconf -- it is a simple program
 specific to Netpbm that prompts you for information about your system.
diff --git a/editor/pnmmargin b/editor/pnmmargin
index b31deefd..0f57d1d4 100755
--- a/editor/pnmmargin
+++ b/editor/pnmmargin
@@ -31,30 +31,30 @@ while true ; do
         plainopt="-plain"
         shift
         ;;
-	-w|-wh|-whi|-whit|-white )
-	color="-white"
-	shift
-	;;
-	-b|-bl|-bla|-blac|-black )
-	color="-black"
-	shift
-	;;
-	-c|-co|-col|-colo|-color )
-	shift
-	if [ ! ${1-""} ] ; then
-       	    echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
-	    exit 1
-	fi
-	color="$1"
-	shift
-	;;
-	-* )
-	echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
-	exit 1
-	;;
-	* )
-	break
-	;;
+        -w|-wh|-whi|-whit|-white )
+        color="-white"
+        shift
+        ;;
+        -b|-bl|-bla|-blac|-black )
+        color="-black"
+        shift
+        ;;
+        -c|-co|-col|-colo|-color )
+        shift
+        if [ ! ${1-""} ] ; then
+            echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
+            exit 1
+        fi
+        color="$1"
+        shift
+        ;;
+        -* )
+        echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
+        exit 1
+        ;;
+        * )
+        break
+        ;;
     esac
 done
 
@@ -82,7 +82,7 @@ if [ $size -eq 0 ] ; then
 else
     # If -white or -black, write output with pnmpad and exit.
     # Otherwise construct spacer files.
-    
+
     case "$color" in
         -gofigure )
         pnmcut 0 0 1 1 $tmp1 | pnmtile $size 1 > $tmp2
@@ -97,12 +97,10 @@ else
         ;;
     esac
     pamflip -rotate90 $tmp2 > $tmp3
-    
+
     # Cat things together.
     pnmcat -lr $tmp2 $tmp1 $tmp2 > $tmp4
     pnmcat -tb $plainopt $tmp3 $tmp4 $tmp3
 fi
 
 
-
-
diff --git a/editor/specialty/ppmrelief.c b/editor/specialty/ppmrelief.c
index 1c408aec..14a6d0a8 100644
--- a/editor/specialty/ppmrelief.c
+++ b/editor/specialty/ppmrelief.c
@@ -1,4 +1,4 @@
-/* ppmrelief.c - generate a relief map of a portable pixmap
+/* ppmrelief.c - generate a relief map of a PPM image
 **
 ** Copyright (C) 1990 by Wilson H. Bent, Jr.
 **
@@ -11,86 +11,104 @@
 */
 
 #include <stdio.h>
+
 #include "pm_c_util.h"
 #include "ppm.h"
 
+
+
+static pixval
+clip(int    const p,
+     pixval const maxval) {
+
+    return MAX(0, MIN(maxval, p));
+}
+
+
+
 int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    pixel** inputbuf;
-    pixel* outputrow;
-    int argn, rows, cols, format, row;
-    register int col;
-    pixval maxval, mv2;
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    pixel ** inputbuf;
+    pixel * outputrow;
+    int argn, format, rows, cols;
+    unsigned int row, col;
+    pixval maxval;
     const char* const usage = "[ppmfile]";
 
-    ppm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     argn = 1;
 
     if ( argn != argc ) {
-        ifp = pm_openr( argv[argn] );
+        ifP = pm_openr( argv[argn] );
         ++argn;
     } else
-        ifp = stdin;
+        ifP = stdin;
 
     if ( argn != argc )
         pm_usage( usage );
-    
-    ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
+
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format );
 
     if (cols < 3 || rows < 3 )
         pm_error("Input image too small: %u x %u.  Must be at least 3x3",
                   cols, rows);
 
-    mv2 = maxval / 2;
-
     /* Allocate space for 3 input rows, plus an output row. */
-    inputbuf = ppm_allocarray( cols, 3 );
-    outputrow = ppm_allocrow( cols );
+    inputbuf  = ppm_allocarray(cols, 3);
+    outputrow = ppm_allocrow(cols);
 
-    ppm_writeppminit( stdout, cols, rows, maxval, 0 );
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
     /* Read in the first two rows. */
-    ppm_readppmrow( ifp, inputbuf[0], cols, maxval, format );
-    ppm_readppmrow( ifp, inputbuf[1], cols, maxval, format );
+    ppm_readppmrow(ifP, inputbuf[0], cols, maxval, format);
+    ppm_readppmrow(ifP, inputbuf[1], cols, maxval, format);
 
     /* Write out the first row, all zeros. */
-    for ( col = 0; col < cols; ++col )
+    for (col = 0; col < cols; ++col)
         PPM_ASSIGN( outputrow[col], 0, 0, 0 );
-    ppm_writeppmrow( stdout, outputrow, cols, maxval, 0 );
+
+    ppm_writeppmrow(stdout, outputrow, cols, maxval, 0);
 
     /* Now the rest of the image - read in the 3rd row of inputbuf,
-    ** and convolve with the first row into the output buffer.
+       and convolve with the first row into the output buffer.
     */
-    for ( row = 2 ; row < rows; ++row ) {
-        pixval r, g, b;
-        int rowa, rowb;
-
-        rowa = row % 3;
-        rowb = (row + 2) % 3;
-        ppm_readppmrow( ifp, inputbuf[rowa], cols, maxval, format );
-        
-        for ( col = 0; col < cols - 2; ++col ) {
-            r = MAX(0, MIN(maxval, PPM_GETR( inputbuf[rowa][col] ) +
-                           ( mv2 - PPM_GETR( inputbuf[rowb][col + 2] ) )));
-            g = MAX(0, MIN(maxval, PPM_GETG( inputbuf[rowa][col] ) +
-                           ( mv2 - PPM_GETG( inputbuf[rowb][col + 2] ) )));
-            b = MAX(0, MIN(maxval, PPM_GETB( inputbuf[rowa][col] ) +
-                           ( mv2 - PPM_GETB( inputbuf[rowb][col + 2] ) )));
-            PPM_ASSIGN( outputrow[col + 1], r, g, b );
+    for (row = 2 ; row < rows; ++row) {
+        pixval       const mv2 = maxval / 2;
+        unsigned int const rowa = row % 3;
+        unsigned int const rowb = (rowa + 2) % 3;
+
+        ppm_readppmrow(ifP, inputbuf[rowa], cols, maxval, format);
+
+        for (col = 0; col < cols - 2; ++col) {
+            pixel const inputA = inputbuf[rowa][col];
+            pixel const inputB = inputbuf[rowb][col + 2];
+            
+            pixval const r =
+                clip(PPM_GETR(inputA) + (mv2 - PPM_GETR(inputB)), maxval);
+            pixval const g =
+                clip(PPM_GETG(inputA) + (mv2 - PPM_GETG(inputB)), maxval);
+            pixval const b =
+                clip(PPM_GETB(inputA) + (mv2 - PPM_GETB(inputB)), maxval);
+
+            PPM_ASSIGN(outputrow[col + 1], r, g, b);
         }
-        ppm_writeppmrow( stdout, outputrow, cols, maxval, 0 );
+        ppm_writeppmrow(stdout, outputrow, cols, maxval, 0);
     }
 
     /* And write the last row, zeros again. */
-    for ( col = 0; col < cols; ++col )
-        PPM_ASSIGN( outputrow[col], 0, 0, 0 );
-    ppm_writeppmrow( stdout, outputrow, cols, maxval, 0 );
+    for (col = 0; col < cols; ++col)
+        PPM_ASSIGN(outputrow[col], 0, 0, 0);
+
+    ppm_writeppmrow(stdout, outputrow, cols, maxval, 0);
+
+    ppm_freerow(outputrow);
+    ppm_freearray(inputbuf, 3);
 
-    pm_close( ifp );
-    pm_close( stdout );
+    pm_close(ifP);
+    pm_close(stdout);
 
-    exit( 0 );
+    return 0;
 }
diff --git a/generator/pgmkernel.c b/generator/pgmkernel.c
index 4f40f003..ec634c16 100644
--- a/generator/pgmkernel.c
+++ b/generator/pgmkernel.c
@@ -174,14 +174,14 @@ writeKernel(FILE *       const ofP,
             unsigned int const halfRows) {
 
     unsigned int row;
-    
+
     pgm_writepgminit(stdout, cols, rows, maxval, 0);
 
     for (row = 0; row < halfRows; ++row)
         pgm_writepgmrow(stdout, halfKernel[row], cols, maxval, 0);
 
     /* Now write out the same rows in reverse order. */
-    
+
     for (; row < rows; ++row)
         pgm_writepgmrow(stdout, halfKernel[rows-1-row], cols, maxval, 0);
 }
@@ -193,12 +193,15 @@ main(int argc, const char * argv[]) {
 
     struct CmdlineInfo cmdline;
     unsigned int arows;
-    int arow;
+    unsigned int arow;
     double xcenter, ycenter;
         /* row, column "number" of center of kernel */
     double tMax;
         /* The maximum t value over all pixels */
-    gray ** destarray;
+    gray ** halfKernel;
+        /* The upper half of the kernel we generate.  The lower half is
+           just the mirror image of this.
+        */
 
     pm_proginit(&argc, argv);
 
@@ -213,27 +216,28 @@ main(int argc, const char * argv[]) {
 
     arows = (cmdline.rows + 1) / 2;
         /* Half the number of rows.  Add 1 if odd. */
-    destarray = pgm_allocarray(cmdline.cols, arows);
+    halfKernel = pgm_allocarray(cmdline.cols, arows);
 
     for (arow = 0; arow < arows; ++arow) {
         double const dy2 = SQR(arow - ycenter);
 
         unsigned int col;
-
-        for (col = 0; col < cmdline.cols; ++col) {
+        for (col = 0; col < (cmdline.cols +1) / 2; ++col) {
             double const dx2 = SQR(col - xcenter);
 
             double const normalized = t(dx2, dy2, cmdline.weight) / 2 / tMax;
 
-            destarray[arow][col] = destarray[arow][cmdline.cols - col - 1] =
-                ROUNDU(cmdline.maxval * (0.5 + normalized));
+            gray const grayval = ROUNDU(cmdline.maxval * (0.5 + normalized));
+
+            halfKernel[arow][col                   ] = grayval;
+            halfKernel[arow][cmdline.cols - col - 1] = grayval;
         }
     }
 
     writeKernel(stdout, cmdline.cols, cmdline.rows, cmdline.maxval,
-                destarray, arows);
+                halfKernel, arows);
 
-    pgm_freearray(destarray, arows);
+    pgm_freearray(halfKernel, arows);
 
     return 0;
 }
diff --git a/generator/pgmmake.c b/generator/pgmmake.c
index e33f64e0..f8f8b09c 100644
--- a/generator/pgmmake.c
+++ b/generator/pgmmake.c
@@ -46,16 +46,18 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free (option_def);
+
     if (!maxvalSpec)
         cmdlineP->maxval = PGM_MAXMAXVAL;
     else {
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 3)
         pm_error("Need 3 arguments: gray level, width, height.");
@@ -82,7 +84,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     gray * grayrow;
-    unsigned int row;
+    unsigned int col, row;
 
     pgm_init(&argc, argv);
 
@@ -91,12 +93,12 @@ main(int argc, char *argv[]) {
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     grayrow = pgm_allocrow(cmdline.cols);
 
-    for (row = 0; row < cmdline.rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cmdline.cols; ++col)
-            grayrow[col] = cmdline.grayLevel;
+    /* All rows are identical.  Fill once. */
+    for (col = 0; col < cmdline.cols; ++col)
+        grayrow[col] = cmdline.grayLevel;
+
+    for (row = 0; row < cmdline.rows; ++row)
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
-	}
 
     pgm_freerow(grayrow);
     pm_close(stdout);
diff --git a/generator/pgmramp.c b/generator/pgmramp.c
index 56d6f8e2..225542fe 100644
--- a/generator/pgmramp.c
+++ b/generator/pgmramp.c
@@ -16,7 +16,7 @@
 #include "pgm.h"
 #include "shhopt.h"
 
-enum ramptype {RT_LR, RT_TB, RT_RECT, RT_ELLIP};
+enum ramptype {RT_LR, RT_TB, RT_DIAG, RT_RECT, RT_ELLIP};
 
 
 struct cmdlineInfo {
@@ -39,7 +39,7 @@ parseCommandLine(int argc, char ** argv,
   program can use easily, struct cmdlineInfo.  Validate arguments along
   the way and exit program with message if invalid.
 
-  Note that some string information we return as *cmdlineP is in the storage 
+  Note that some string information we return as *cmdlineP is in the storage
   argv[] points to.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc(100*sizeof(optEntry));
@@ -47,13 +47,14 @@ parseCommandLine(int argc, char ** argv,
          */
     optStruct3 opt;
 
-    unsigned int lrSpec, tbSpec, rectangleSpec, ellipseSpec;
+    unsigned int lrSpec, tbSpec, diagonalSpec, rectangleSpec, ellipseSpec;
     unsigned int maxvalSpec;
     unsigned int option_def_index;
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "lr",        OPT_FLAG, NULL,              &lrSpec,        0);
     OPTENT3(0,   "tb",        OPT_FLAG, NULL,              &tbSpec,        0);
+    OPTENT3(0,   "diagonal",  OPT_FLAG, NULL,              &diagonalSpec,  0);
     OPTENT3(0,   "rectangle", OPT_FLAG, NULL,              &rectangleSpec, 0);
     OPTENT3(0,   "ellipse",   OPT_FLAG, NULL,              &ellipseSpec,   0);
     OPTENT3(0,   "maxval",    OPT_UINT, &cmdlineP->maxval, &maxvalSpec,    0);
@@ -65,15 +66,20 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (lrSpec + tbSpec + rectangleSpec + ellipseSpec == 0)
-        pm_error("You must specify one of -lr, -tb, -rectangle, or -ellipse");
-    if (lrSpec + tbSpec + rectangleSpec + ellipseSpec > 1)
+    free (option_def);
+
+    if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec == 0)
+        pm_error("You must specify one of "
+                 "-lr, -tb, -diagonal, -rectangle, or -ellipse");
+    if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec > 1)
         pm_error("You may specify at most one of "
-                 "-lr, -tb, -rectangle, or -ellipse");
+                 "-lr, -tb, -diagonal, -rectangle, or -ellipse");
     if (lrSpec)
         cmdlineP->ramptype = RT_LR;
     else if (tbSpec)
         cmdlineP->ramptype = RT_TB;
+    else if (diagonalSpec)
+        cmdlineP->ramptype = RT_DIAG;
     else if (rectangleSpec)
         cmdlineP->ramptype = RT_RECT;
     else if (ellipseSpec)
@@ -87,10 +93,10 @@ parseCommandLine(int argc, char ** argv,
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 2)
         pm_error("Need two arguments: width and height.");
@@ -105,7 +111,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-int 
+int
 main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
@@ -119,31 +125,36 @@ main(int argc, char *argv[]) {
 
     colso2 = MAX(1, cmdline.cols / 2);
     rowso2 = MAX(1, cmdline.rows / 2);
-    
+
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     grayrow = pgm_allocrow(cmdline.cols);
-    
+
     for (row = 0; row < cmdline.rows; ++row) {
         unsigned int col;
         for (col = 0; col < cmdline.cols; ++col) {
             switch (cmdline.ramptype) {
             case RT_LR:
-                grayrow[col] = 
-                    col * cmdline.maxval / MAX(cmdline.cols-1, 1);
+                /* Fill row buffer once.  All rows are identical. */
+                if (row == 0)
+                    grayrow[col] =
+                        (float) col * cmdline.maxval / MAX(cmdline.cols-1, 1);
                 break;
             case RT_TB:
-                grayrow[col] = 
-                    row * cmdline.maxval / MAX(cmdline.rows-1, 1);
+                grayrow[col] =
+                    (float) row * cmdline.maxval / MAX(cmdline.rows-1, 1);
+                break;
+            case RT_DIAG:
+                grayrow[col] =
+                    ((float) col + row) * cmdline.maxval /
+                        MAX((float) cmdline.cols + cmdline.rows-2, 1);
                 break;
-
             case RT_RECT: {
                 float const r = fabs((int)(rowso2 - row)) / rowso2;
                 float const c = fabs((int)(colso2 - col)) / colso2;
-                grayrow[col] = 
+                grayrow[col] =
                     cmdline.maxval - (r + c) / 2.0 * cmdline.maxval;
-            }
-            break;
-            
+            } break;
+
             case RT_ELLIP: {
                 float const r = fabs((int)(rowso2 - row)) / rowso2;
                 float const c = fabs((int)(colso2 - col)) / colso2;
@@ -153,12 +164,11 @@ main(int argc, char *argv[]) {
                 if ( v < 0.0 ) v = 0.0;
                 else if ( v > 1.0 ) v = 1.0;
                 grayrow[col] = cmdline.maxval - v * cmdline.maxval;
+            } break;
             }
-            break;
-            }
-	    }
+        }
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
-	}
+    }
 
     pgm_freerow(grayrow);
     pm_close(stdout);
diff --git a/generator/ppmmake.c b/generator/ppmmake.c
index 9c8a2edb..2d4bbca8 100644
--- a/generator/ppmmake.c
+++ b/generator/ppmmake.c
@@ -58,16 +58,18 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free (option_def);
+
     if (!maxvalSpec)
         cmdlineP->maxval = PPM_MAXMAXVAL;
     else {
         if (cmdlineP->maxval > PPM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PPM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 3)
         pm_error("Need 3 arguments: color, width, height.");
@@ -88,7 +90,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     pixel * pixrow;
-    unsigned int row;
+    unsigned int row, col;
 
     ppm_init(&argc, argv);
 
@@ -97,12 +99,12 @@ main(int argc, char *argv[]) {
     ppm_writeppminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     pixrow = ppm_allocrow(cmdline.cols);
 
-    for (row = 0; row < cmdline.rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cmdline.cols; ++col)
-            pixrow[col] = cmdline.color;
+    /* All rows are identical.  Fill once. */
+    for (col = 0; col < cmdline.cols; ++col)
+        pixrow[col] = cmdline.color;
+
+    for (row = 0; row < cmdline.rows; ++row)
         ppm_writeppmrow(stdout, pixrow, cmdline.cols, cmdline.maxval, 0);
-	}
 
     ppm_freerow(pixrow);
     pm_close(stdout);
diff --git a/lib/Makefile b/lib/Makefile
index 6512949f..d5f47813 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -13,7 +13,7 @@ else
 LIBNETPBM = $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).$(NETPBMLIBSUFFIX)
 endif
 
-ifeq ($(STATICLIB_TOO),y)
+ifeq ($(STATICLIB_TOO),Y)
 EXTRA_STATICLIB = libnetpbm.$(STATICLIBSUFFIX)
 else
 EXTRA_STATICLIB =
@@ -130,7 +130,7 @@ libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN): $(LIBOBJECTS) $(LIBOBJECTS_X)
 endif
 
 ifeq ($(NETPBMLIBTYPE),dll)
-ifeq ($(STATICLIB_TOO),y)
+ifeq ($(STATICLIB_TOO),Y)
 $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll: $(LIBOBJECTS) $(LIBOBJECTS_X) libnetpbm.$(STATICLIBSUFFIX)
 else
 $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll: $(LIBOBJECTS) $(LIBOBJECTS_X)
@@ -170,16 +170,16 @@ endif
 # STATICLIB_SUFFIX may just be arbitrary.
 #-----------------------------------------------------------------------------
 ifeq ($(NETPBMLIBTYPE),unixstatic)
-  BUILD_STATICLIB = y
+  BUILD_STATICLIB = Y
 else
-  ifeq ($(STATICLIB_TOO),y)
-    BUILD_STATICLIB = y
+  ifeq ($(STATICLIB_TOO),Y)
+    BUILD_STATICLIB = Y
   else
     BUILD_STATICLIB = n
   endif
 endif
 
-ifeq ($(BUILD_STATICLIB),y)
+ifeq ($(BUILD_STATICLIB),Y)
 libnetpbm.$(STATICLIBSUFFIX): $(LIBOBJECTS) $(LIBOBJECTS_X)
 	-rm -f $@
 	$(AR) rc $@ $(LIBOBJECTS) $(LIBOBJECTS_X)
@@ -190,7 +190,7 @@ endif
 # To avoid major hassles with having ppmdcfont available here, we just
 # ship a pre-made standardppmfont.c, so this rule will not normally be
 # used.
-standardppmdfont.c:standard.ppmdfont
+standardppmdfont.c: standard.ppmdfont
 	ppmdcfont <$< >$@
 
 # Note that we create a new compile.h only for the first make after a
diff --git a/lib/libpamread.c b/lib/libpamread.c
index 3c8e2e33..cbac18c1 100644
--- a/lib/libpamread.c
+++ b/lib/libpamread.c
@@ -202,6 +202,51 @@ parse4BpsRow(const struct pam *    const pamP,
 
 
 static void
+validatePamRow(const struct pam * const pamP,
+               tuple *            const tuplerow,
+               const char **      const errorP) {
+/*----------------------------------------------------------------------------
+  Check for sample values above maxval in input.  
+
+  Note: a program that wants to deal with invalid sample values itself can
+  simply make sure it sets pamP->maxval sufficiently high, so this validation
+  never fails.
+-----------------------------------------------------------------------------*/
+    /* To save time, skip the test for if the maxval is a saturated value
+       (255, 65535) or format is PBM.
+
+       This is an expensive test, but is skipped in most cases: in practice
+       maxvals other than 255 or 65535 are uncommon.  Thus we do this in a
+       separate pass through the row rather than while reading in the row.
+    */
+
+    if (pamP->maxval == (((sample) 0x1) << pamP->bytes_per_sample*8) - 1 ||
+        PAM_FORMAT_TYPE(pamP->format) == PBM_FORMAT) {
+        /* There's no way a sample can be invalid, so we don't need to
+           look at the samples individually.
+        */
+        *errorP = NULL;
+    } else {
+        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) {
+                    pm_asprintf(errorP,
+                                "Plane %u sample value %lu exceeds the "
+                                "image maxval of %lu",
+                                plane, tuplerow[col][plane], pamP->maxval);
+                    return;
+                }
+            }
+        }
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
 readRawNonPbmRow(const struct pam * const pamP,
                  tuple *            const tuplerow) {
 
@@ -234,8 +279,10 @@ readRawNonPbmRow(const struct pam * const pamP,
             case 4: parse4BpsRow(pamP, tuplerow, inbuf); break;
             default:
                 pm_asprintf(&error, "invalid bytes per sample passed to "
-                            "pnm_formatpamrow(): %u",  pamP->bytes_per_sample);
+                            "pnm_formatpamrow(): %u", pamP->bytes_per_sample);
             }
+            if (error == NULL)
+                validatePamRow(pamP, tuplerow, &error);
         }
     }
     pnm_freerowimage(inbuf);
@@ -264,7 +311,7 @@ pnm_readpamrow(const struct pam * const pamP,
     /* 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 
-       pnm_writepaminit().
+       pnm_readpaminit().
     */  
 
     /* Need a special case for raw PBM because it has multiple tuples (8)
diff --git a/lib/libpgm1.c b/lib/libpgm1.c
index 987ee04e..57344c16 100644
--- a/lib/libpgm1.c
+++ b/lib/libpgm1.c
@@ -100,10 +100,13 @@ validateComputableSize(unsigned int const cols,
    you expect.  That failed expectation can be disastrous if you use
    it to allocate memory.
 
+   It is very normal to allocate space for a pixel row, so we make sure
+   the size of a pixel row, in bytes, can be represented by an 'int'.
+
    A common operation is adding 1 or 2 to the highest row or
    column number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 2)
+    if (cols > INT_MAX / (sizeof(gray)) || cols > INT_MAX - 2)
         pm_error("image width (%u) too large to be processed", cols);
     if (rows > INT_MAX - 2)
         pm_error("image height (%u) too large to be processed", rows);
@@ -172,11 +175,44 @@ pgm_readpgminit(FILE * const fileP,
 
 
 static void
+validateRpgmRow(gray *         const grayrow, 
+                unsigned int   const cols,
+                gray           const maxval,
+                const char **  const errorP) {
+/*----------------------------------------------------------------------------
+  Check for sample values above maxval in input.  
+
+  Note: a program that wants to deal with invalid sample values itself can
+  simply make sure it uses a sufficiently high maxval on the read function
+  call, so this validation never fails.
+-----------------------------------------------------------------------------*/
+    if (maxval == 255 || maxval == 65535) {
+        /* There's no way a sample can be invalid, so we don't need to look at
+           the samples individually.
+        */
+        *errorP = NULL;
+    } else {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            if (grayrow[col] > maxval) {
+                pm_asprintf(errorP,
+                            "gray value %u is greater than maxval (%u)",
+                            grayrow[col], maxval);
+                return;
+            }
+        }
+        *errorP = NULL;
+    }
+}  
+
+
+
+static void
 readRpgmRow(FILE * const fileP,
-               gray * const grayrow, 
-               int    const cols,
-               gray   const maxval,
-               int    const format) {
+            gray * const grayrow, 
+            int    const cols,
+            gray   const maxval,
+            int    const format) {
 
     unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
     int          const bytesPerRow    = cols * bytesPerSample;
@@ -198,7 +234,6 @@ readRpgmRow(FILE * const fileP,
             pm_asprintf(&error, "Error reading row.  Short read of %u bytes "
                         "instead of %u", (unsigned)rc, bytesPerRow);
         else {
-            error = NULL;
             if (maxval < 256) {
                 unsigned int col;
                 for (col = 0; col < cols; ++col)
@@ -218,6 +253,7 @@ readRpgmRow(FILE * const fileP,
                     grayrow[col] = g;
                 }
             }
+            validateRpgmRow(grayrow, cols, maxval, &error); 
         }
         free(rowBuffer);
     }
@@ -241,7 +277,7 @@ readPbmRow(FILE * const fileP,
     jmp_buf * origJmpbufP;
     bit * bitrow;
     
-    bitrow = pbm_allocrow(cols);
+    bitrow = pbm_allocrow_packed(cols);
     if (setjmp(jmpbuf) != 0) {
         pbm_freerow(bitrow);
         pm_setjmpbuf(origJmpbufP);
@@ -251,10 +287,14 @@ readPbmRow(FILE * const fileP,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        pbm_readpbmrow(fileP, bitrow, cols, format);
-        for (col = 0; col < cols; ++col)
-            grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0;
+        pbm_readpbmrow_packed(fileP, bitrow, cols, format);
 
+        for (col = 0; col < cols; ++col) {
+            grayrow[col] =
+                ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
+                maxval : 0
+                ;
+        }
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
diff --git a/lib/libpnm1.c b/lib/libpnm1.c
index 536e5dc4..29fee13a 100644
--- a/lib/libpnm1.c
+++ b/lib/libpnm1.c
@@ -72,10 +72,13 @@ validateComputableSize(unsigned int const cols,
    you expect.  That failed expectation can be disastrous if you use
    it to allocate memory.
 
+   It is very normal to allocate space for a pixel row, so we make sure
+   the size of a pixel row, in bytes, can be represented by an 'int'.
+
    A common operation is adding 1 or 2 to the highest row or
    column number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 2)
+    if (cols > INT_MAX/(sizeof(pixval) * 3) || cols > INT_MAX - 2)
         pm_error("image width (%u) too large to be processed", cols);
     if (rows > INT_MAX - 2)
         pm_error("image height (%u) too large to be processed", rows);
@@ -178,10 +181,10 @@ readpbmrow(FILE * const fileP,
     jmp_buf * origJmpbufP;
     bit * bitrow;
 
-    bitrow = pbm_allocrow(cols);
+    bitrow = pbm_allocrow_packed(cols);
 
     if (setjmp(jmpbuf) != 0) {
-        pbm_freerow(bitrow);
+        pbm_freerow_packed(bitrow);
         pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
     } else {
@@ -189,11 +192,14 @@ readpbmrow(FILE * const fileP,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        pbm_readpbmrow(fileP, bitrow, cols, format);
-
-        for (col = 0; col < cols; ++col)
-            PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0 : maxval);
+        pbm_readpbmrow_packed(fileP, bitrow, cols, format);
 
+        for (col = 0; col < cols; ++col) {
+            pixval const g =
+                ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
+                maxval : 0;
+            PNM_ASSIGN1(xelrow[col], g);
+        }
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
diff --git a/lib/libppm1.c b/lib/libppm1.c
index 195aec70..ff940540 100644
--- a/lib/libppm1.c
+++ b/lib/libppm1.c
@@ -100,10 +100,13 @@ validateComputableSize(unsigned int const cols,
    you expect.  That failed expectation can be disastrous if you use
    it to allocate memory.
 
+   It is very normal to allocate space for a pixel row, so we make sure
+   the size of a pixel row, in bytes, can be represented by an 'int'.
+
    A common operation is adding 1 or 2 to the highest row or
    column number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 2)
+    if (cols > INT_MAX/(sizeof(pixval) * 3) || cols > INT_MAX - 2)
         pm_error("image width (%u) too large to be processed", cols);
     if (rows > INT_MAX - 2)
         pm_error("image height (%u) too large to be processed", rows);
@@ -224,6 +227,52 @@ interpRasterRowRaw(const unsigned char * const rowBuffer,
 
 
 static void
+validateRppmRow(pixel *       const pixelrow,
+                unsigned int  const cols,
+                pixval        const maxval,
+                const char ** const errorP) {
+/*----------------------------------------------------------------------------
+  Check for sample values above maxval in input.  
+
+  Note: a program that wants to deal with invalid sample values itself can
+  simply make sure it uses a sufficiently high maxval on the read function
+  call, so this validation never fails.
+-----------------------------------------------------------------------------*/
+    if (maxval == 255 || maxval == 65535) {
+        /* There's no way a sample can be invalid, so we don't need to look at
+           the samples individually.
+        */
+        *errorP = NULL;
+    } else {
+        unsigned int col;
+
+        for (col = 0, *errorP = NULL; col < cols && !*errorP; ++col) {
+            pixval const r = PPM_GETR(pixelrow[col]);
+            pixval const g = PPM_GETG(pixelrow[col]);
+            pixval const b = PPM_GETB(pixelrow[col]);
+
+            if (r > maxval)
+                pm_asprintf(
+                    errorP,
+                    "Red sample value %u is greater than maxval (%u)",
+                    r, maxval);
+            else if (g > maxval)
+                pm_asprintf(
+                    errorP,
+                    "Green sample value %u is greater than maxval (%u)",
+                    g, maxval);
+            else if (b > maxval)
+                pm_asprintf(
+                    errorP,
+                    "Blue sample value %u is greater than maxval (%u)",
+                    b, maxval);
+        }
+    }
+}
+
+
+
+static void
 readRppmRow(FILE *       const fileP, 
             pixel *      const pixelrow, 
             unsigned int const cols, 
@@ -259,7 +308,8 @@ readRppmRow(FILE *       const fileP,
                             "instead of %u", (unsigned)bytesRead, bytesPerRow);
             else {
                 interpRasterRowRaw(rowBuffer, pixelrow, cols, bytesPerSample);
-                error = NULL;
+
+                validateRppmRow(pixelrow, cols, maxval, &error);
             }
         }
         free(rowBuffer);
@@ -319,10 +369,10 @@ readPbmRow(FILE *       const fileP,
     jmp_buf * origJmpbufP;
     bit * bitrow;
 
-    bitrow = pbm_allocrow(cols);
+    bitrow = pbm_allocrow_packed(cols);
 
     if (setjmp(jmpbuf) != 0) {
-        pbm_freerow(bitrow);
+        pbm_freerow_packed(bitrow);
         pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
     } else {
@@ -330,10 +380,12 @@ readPbmRow(FILE *       const fileP,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        pbm_readpbmrow(fileP, bitrow, cols, format);
+        pbm_readpbmrow_packed(fileP, bitrow, cols, format);
 
         for (col = 0; col < cols; ++col) {
-            pixval const g = (bitrow[col] == PBM_WHITE) ? maxval : 0;
+            pixval const g =
+                ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
+                maxval : 0;
             PPM_ASSIGN(pixelrow[col], g, g, g);
         }
         pm_setjmpbuf(origJmpbufP);
diff --git a/lib/standardppmdfont.c b/lib/standardppmdfont.c
index aa4707e2..bc0c8ff8 100644
--- a/lib/standardppmdfont.c
+++ b/lib/standardppmdfont.c
@@ -1,4 +1,4 @@
-/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a ppmfont file. */
+/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a ppmdfont file. */
 
 #include "ppmdfont.h"
 
diff --git a/other/Makefile b/other/Makefile
index 1c25d6a6..69562be0 100644
--- a/other/Makefile
+++ b/other/Makefile
@@ -24,8 +24,8 @@ endif
 # build.
 
 PORTBINARIES = pamarith pambayer pamchannel pamdepth \
-	pamendian pamexec pamfixtrunc pamlookup pampick pamsplit \
-	pamstack pamsummcol pnmcolormap \
+	pamendian pamexec pamfix pamlookup pampick pamsplit \
+	pamstack pamsummcol pamvalidate pnmcolormap \
 	ppmdcfont ppmddumpfont ppmdmkfont 
 
 BINARIES = $(PORTBINARIES)
@@ -34,7 +34,7 @@ ifneq ($(LINUXSVGALIB),NONE)
   BINARIES += ppmsvgalib
 endif
 
-SCRIPTS = ppmtomap
+SCRIPTS = ppmtomap pamfixtrunc
 
 OBJECTS = $(BINARIES:%=%.o)
 
diff --git a/other/pamchannel.c b/other/pamchannel.c
index 3adb0e66..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.
     */
@@ -37,7 +37,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** 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.
@@ -176,7 +176,7 @@ doOneImage(FILE *       const ifP,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     int eof;
 
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 8d58b447..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 */
-
-    pm_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/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/test/411toppm.test b/test/411toppm.test
index ce374f29..335cdcc8 100755
--- a/test/411toppm.test
+++ b/test/411toppm.test
@@ -2,6 +2,9 @@
 # This script tests: 411toppm
 # Also requires: 
 
+  alias 411toppm="${PBM_TESTPREFIX}411toppm"
+  shopt -s expand_aliases
+
 # Test 1. should produce: 240376509 9229
 # The above value is what 411toppm has been always producing.
 # 411toppm's author Steve Allen says he was not able to obtain accurate
@@ -9,4 +12,4 @@
 #
 # See comment at head of source file 411toppm.c.
 
-head -c 4608 /dev/zero | ${PBM_TESTPREFIX}411toppm -quiet | cksum
+head -c 4608 /dev/zero | 411toppm -quiet | cksum
diff --git a/test/Execute-Tests b/test/Execute-Tests
index 7a1a3793..68dd5581 100755
--- a/test/Execute-Tests
+++ b/test/Execute-Tests
@@ -118,12 +118,25 @@ export PATH=${srcdir}:$PATH
 # By default the tests are executed in the order described in the
 # file Test-Order.  Copy this file from the source directory
 # to the work directory.
+#
+# The string "target" is a comma-separated list of target programs.
+# When set only tests for programs in the list will be run.
 # 
 # The --no-clobber version comes useful when the user wants a modified
 # (pared-down) version of Test-Order.
 
-cp ${srcdir}/Test-Order ./Test-Order
-#cp --no-clobber ${srcdir}/Test-Order ./Test-Order
+if [ ! -z $target ]
+  then echo $target | sed 's/,/\n/g' | \
+                       sed 's/^/\${PBM_TESTPREFIX}/' | \
+                       grep -f - ${srcdir}/*.test -l | \
+                       while read i ; do echo ${i##*/} ; done | 
+                       grep -f - ${srcdir}/Test-Order > ./Test-Order ; 
+  else 
+       cp ${srcdir}/Test-Order ./Test-Order ;
+       #cp --no-clobber ${srcdir}/Test-Order ./Test-Order ;
+fi
+
+#let array[5]=0
 
 for t in `grep -v "^#" ./Test-Order | fgrep ".test"`
 do
@@ -134,6 +147,7 @@ case $result in
      if [ $? -eq 0 ]
         then let result=0;  rm  ${t%.test}.out ;
         else let result=1;
+             grep "^##" ${srcdir}/$t   # Print failure message.
      fi
      let testable=1 ;;
 80) let result=4 ; let testable=0;;
@@ -173,7 +187,7 @@ echo ==================
 
 for s in 0 1 2 3 4 5
   do
-    if [[ ${array[${s}]} -gt 0 || s -eq 1 ]]
+    if [[ ${array[${s}]} -gt 0 || s -eq 1 || s -eq 5 ]]
     then echo ${status[${s}]} ${array[${s}]}
     fi
   done
diff --git a/test/Test-Order b/test/Test-Order
index 31cd5324..ff4b9855 100644
--- a/test/Test-Order
+++ b/test/Test-Order
@@ -61,12 +61,17 @@ pnmremap2.test
 pnmtile.test
 ppmbrighten.test
 ppmdither.test
+ppmrelief.test
 pamedge.test
 ppmdim.test
 pnmshear.test
 
 ppmmix.test
 
+# Symmetry test
+
+symmetry.test
+
 # Format converter tests
 
 pbmtog3.test
@@ -104,6 +109,7 @@ gem-roundtrip.test
 gif-roundtrip.test
 gif-quant-roundtrip.test
 hdiff-roundtrip.test
+jbig-roundtrip.test
 leaf-roundtrip.test
 mgr-roundtrip.test
 mrf-roundtrip.test
diff --git a/test/atk-roundtrip.test b/test/atk-roundtrip.test
index f64b0c3c..3d15eec4 100755
--- a/test/atk-roundtrip.test
+++ b/test/atk-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtoatk atktopbm
 # Also requires: 
 
+  alias atktopbm="${PBM_TESTPREFIX}atktopbm"
+  alias pbmtoatk="${PBM_TESTPREFIX}pbmtoatk"
+  shopt -s expand_aliases
+
 # Should print 2425386270 41, cksum of testgrid.pbm
-${PBM_TESTPREFIX}pbmtoatk testgrid.pbm | ${PBM_TESTPREFIX}atktopbm | cksum
+pbmtoatk testgrid.pbm | atktopbm | cksum
diff --git a/test/avs-roundtrip.test b/test/avs-roundtrip.test
index 33f9e581..44b4d8a4 100755
--- a/test/avs-roundtrip.test
+++ b/test/avs-roundtrip.test
@@ -2,6 +2,11 @@
 # This script tests: pamtoavs avstopam
 # Also requires: pamtopnm
 
+  alias avstopam="${PBM_TESTPREFIX}avstopam"
+  alias pamtoavs="${PBM_TESTPREFIX}pamtoavs"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  shopt -s expand_aliases
+
 # Should produce 1926073387 101484, cksum of testimg.ppm
-${PBM_TESTPREFIX}pamtoavs testimg.ppm | \
-  ${PBM_TESTPREFIX}avstopam | ${PBM_BINPREFIX}pamtopnm | cksum
+pamtoavs testimg.ppm | \
+  avstopam | pamtopnm | cksum
diff --git a/test/bmp-roundtrip.test b/test/bmp-roundtrip.test
index af0e6948..291f2669 100755
--- a/test/bmp-roundtrip.test
+++ b/test/bmp-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: bmptopnm ppmtobmp
 # Also requires: 
 
-${PBM_TESTPREFIX}ppmtobmp testimg.ppm | ${PBM_TESTPREFIX}bmptopnm | cksum
-${PBM_TESTPREFIX}ppmtobmp testgrid.pbm | ${PBM_TESTPREFIX}bmptopnm | cksum
\ No newline at end of file
+  alias bmptopnm="${PBM_TESTPREFIX}bmptopnm"
+  alias ppmtobmp="${PBM_TESTPREFIX}ppmtobmp"
+  shopt -s expand_aliases
+
+ppmtobmp testimg.ppm | bmptopnm | cksum
+ppmtobmp testgrid.pbm | bmptopnm | cksum
diff --git a/test/cmuw-roundtrip.test b/test/cmuw-roundtrip.test
index 85de9706..ebb5eecf 100755
--- a/test/cmuw-roundtrip.test
+++ b/test/cmuw-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtocmuwm cmuwmtopbm
 # Also requires: 
 
+  alias cmuwmtopbm="${PBM_TESTPREFIX}cmuwmtopbm"
+  alias pbmtocmuwm="${PBM_TESTPREFIX}pbmtocmuwm"
+  shopt -s expand_aliases
+
 # Should print 2425386270 41, cksum of testgrid.pbm
-${PBM_TESTPREFIX}pbmtocmuwm testgrid.pbm | ${PBM_TESTPREFIX}cmuwmtopbm | cksum
+pbmtocmuwm testgrid.pbm | cmuwmtopbm | cksum
diff --git a/test/cut-paste-roundtrip.test b/test/cut-paste-roundtrip.test
index d5d5989f..15e53219 100755
--- a/test/cut-paste-roundtrip.test
+++ b/test/cut-paste-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pamcut pnmpaste
 # Also requires: 
 
-${PBM_TESTPREFIX}pamcut 50 40 100 70 testimg.ppm | \
-${PBM_TESTPREFIX}pnmpaste -replace - 50 40 testimg.ppm | cksum
+  alias pamcut="${PBM_TESTPREFIX}pamcut"
+  alias pnmpaste="${PBM_TESTPREFIX}pnmpaste"
+  shopt -s expand_aliases
+
+pamcut 50 40 100 70 testimg.ppm | \
+pnmpaste -replace - 50 40 testimg.ppm | cksum
diff --git a/test/eyuvtoppm.test b/test/eyuvtoppm.test
index 5009d3ba..79c30237 100755
--- a/test/eyuvtoppm.test
+++ b/test/eyuvtoppm.test
@@ -2,6 +2,9 @@
 # This script tests: eyuvtoppm
 # Also requires: 
 
+  alias eyuvtoppm="${PBM_TESTPREFIX}eyuvtoppm"
+  shopt -s expand_aliases
+
 # Should produce 1719878124 253455
 
-head -c 126720 /dev/zero |  ${PBM_TESTPREFIX}eyuvtoppm -quiet | cksum
+head -c 126720 /dev/zero |  eyuvtoppm -quiet | cksum
diff --git a/test/facesaver-roundtrip.test b/test/facesaver-roundtrip.test
index 410ba897..362edaf5 100755
--- a/test/facesaver-roundtrip.test
+++ b/test/facesaver-roundtrip.test
@@ -2,7 +2,12 @@
 # This script tests: pgmtofs fstopgm
 # Also requires: ppmtopgm
 
+  alias fstopgm="${PBM_TESTPREFIX}fstopgm"
+  alias pgmtofs="${PBM_TESTPREFIX}pgmtofs"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Should produce 2871603838 33838, cksum of testimg.pgm
 
-${PBM_BINPREFIX}ppmtopgm testimg.ppm | \
-  ${PBM_TESTPREFIX}pgmtofs | ${PBM_TESTPREFIX}fstopgm | cksum
\ No newline at end of file
+ppmtopgm testimg.ppm | \
+  pgmtofs | fstopgm | cksum
diff --git a/test/fits-roundtrip.test b/test/fits-roundtrip.test
index 0c244c5a..65589823 100755
--- a/test/fits-roundtrip.test
+++ b/test/fits-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pamtofits fitstopnm
 # Also requires: 
 
+  alias fitstopnm="${PBM_TESTPREFIX}fitstopnm"
+  alias pamtofits="${PBM_TESTPREFIX}pamtofits"
+  shopt -s expand_aliases
+
 # Should produce 1926073387 101484, cksum of testimg.ppm
-${PBM_TESTPREFIX}pamtofits testimg.ppm | ${PBM_TESTPREFIX}fitstopnm | cksum
+pamtofits testimg.ppm | fitstopnm | cksum
diff --git a/test/g3-roundtrip.test b/test/g3-roundtrip.test
index e00997f6..323a26bf 100755
--- a/test/g3-roundtrip.test
+++ b/test/g3-roundtrip.test
@@ -2,14 +2,19 @@
 # This script tests: g3topbm pbmtog3
 # Also requires: pnmcrop
 
-${PBM_TESTPREFIX}pbmtog3 -nofixedwidth testgrid.pbm | \
-${PBM_TESTPREFIX}g3topbm -width=14 | cmp -s - testgrid.pbm
+  alias g3topbm="${PBM_TESTPREFIX}g3topbm"
+  alias pbmtog3="${PBM_TESTPREFIX}pbmtog3"
+  alias pnmcrop="${PBM_BINPREFIX}pnmcrop"
+  shopt -s expand_aliases
+
+pbmtog3 -nofixedwidth testgrid.pbm | \
+g3topbm -width=14 | cmp -s - testgrid.pbm
 echo $?
 
-${PBM_TESTPREFIX}pbmtog3 -nofixedwidth -reversebits testgrid.pbm | \
-${PBM_TESTPREFIX}g3topbm -width=14 -reversebits | cmp -s - testgrid.pbm
+pbmtog3 -nofixedwidth -reversebits testgrid.pbm | \
+g3topbm -width=14 -reversebits | cmp -s - testgrid.pbm
 echo $?
 
-${PBM_TESTPREFIX}pbmtog3 testgrid.pbm | \
-${PBM_TESTPREFIX}g3topbm  | ${PBM_BINPREFIX}pnmcrop -white -right -bottom | \
+pbmtog3 testgrid.pbm | \
+g3topbm  | pnmcrop -white -right -bottom | \
  cmp -s - testgrid.pbm ; echo $?
diff --git a/test/gem-roundtrip.test b/test/gem-roundtrip.test
index 05f1d28c..97f894c2 100755
--- a/test/gem-roundtrip.test
+++ b/test/gem-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtogem gemtopbm
 # Also requires: gemtopnm
 
+  alias gemtopbm="${PBM_TESTPREFIX}gemtopbm"
+  alias pbmtogem="${PBM_TESTPREFIX}pbmtogem"
+  shopt -s expand_aliases
+
 # Should print 2425386270 41, cksum of testgrid.pbm
-${PBM_TESTPREFIX}pbmtogem testgrid.pbm | ${PBM_TESTPREFIX}gemtopbm | cksum
+pbmtogem testgrid.pbm | gemtopbm | cksum
diff --git a/test/gif-quant-roundtrip.test b/test/gif-quant-roundtrip.test
index 5e093b29..b5f69edd 100755
--- a/test/gif-quant-roundtrip.test
+++ b/test/gif-quant-roundtrip.test
@@ -2,12 +2,17 @@
 # This script tests: giftopnm pamtogif pnmquant
 # Also requires: pnmcolormap pnmremap
 
+  alias giftopnm="${PBM_TESTPREFIX}giftopnm"
+  alias pamtogif="${PBM_TESTPREFIX}pamtogif"
+  alias pnmquant="${PBM_TESTPREFIX}pnmquant"
+  shopt -s expand_aliases
+
 # Should print 0
 
 colors=15      # any value between 2 - 256 works
 
-${PBM_TESTPREFIX}pnmquant $colors testimg.ppm > ${tmpdir}/quant.ppm &&
-${PBM_TESTPREFIX}pamtogif ${tmpdir}/quant.ppm | ${PBM_TESTPREFIX}giftopnm | \
+pnmquant $colors testimg.ppm > ${tmpdir}/quant.ppm &&
+pamtogif ${tmpdir}/quant.ppm | giftopnm | \
    cmp -s - ${tmpdir}/quant.ppm > /dev/null
 echo $?
 
diff --git a/test/gif-roundtrip.ok b/test/gif-roundtrip.ok
index cf95bb37..ece7593c 100644
--- a/test/gif-roundtrip.ok
+++ b/test/gif-roundtrip.ok
@@ -1,4 +1,11 @@
 2871603838 33838
+2871603838 33838
+2871603838 33838
+2871603838 33838
 1926073387 101484
 2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
 P1 4 1 0101 
\ No newline at end of file
diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test
index a32d574a..574638ac 100755
--- a/test/gif-roundtrip.test
+++ b/test/gif-roundtrip.test
@@ -2,11 +2,28 @@
 # This script tests: giftopnm pamtogif
 # Also requires: ppmtopgm ppmtorgb3 rgb3toppm pbmmake
 
+  alias giftopnm="${PBM_TESTPREFIX}giftopnm"
+  alias pamtogif="${PBM_TESTPREFIX}pamtogif"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  alias ppmtorgb3="${PBM_BINPREFIX}ppmtorgb3"
+  alias rgb3toppm="${PBM_BINPREFIX}rgb3toppm"
+  alias pnminvert="${PBM_BINPREFIX}pnminvert"
+  shopt -s expand_aliases
+
 # Test 1. Should produce 2871603838 33838
 # which is the result of ppmtopgm testimg.ppm | cksum
+# four times
+
+ppmtopgm testimg.ppm | tee ${tmpdir}/testimg.pgm | pamtogif | giftopnm | cksum
+pamtogif -interlace ${tmpdir}/testimg.pgm | giftopnm | cksum
+pamtogif -sort ${tmpdir}/testimg.pgm | tee ${tmpdir}/testimg.gif | \
+  giftopnm | cksum
+echo "junk" >> ${tmpdir}/testimg.gif && \
+  giftopnm -image=1 -quitearly ${tmpdir}/testimg.gif | cksum
 
-${PBM_BINPREFIX}ppmtopgm testimg.ppm | \
-  ${PBM_TESTPREFIX}pamtogif | ${PBM_TESTPREFIX}giftopnm | cksum
+rm  ${tmpdir}/testimg.pgm
+rm  ${tmpdir}/testimg.gif
 
 # Test 2. Break up input image into three monochrome planes,
 # maxval 255.  Transform each plane to gif and back to pgm.
@@ -14,26 +31,32 @@ ${PBM_BINPREFIX}ppmtopgm testimg.ppm | \
 # Should print 1926073387 101484
 
 cp testimg.ppm ${tmpdir} &&
-${PBM_BINPREFIX}ppmtorgb3 ${tmpdir}/testimg.ppm &&
-${PBM_TESTPREFIX}pamtogif ${tmpdir}/testimg.red | \
-  ${PBM_TESTPREFIX}giftopnm > ${tmpdir}/out.red &&
-${PBM_TESTPREFIX}pamtogif ${tmpdir}/testimg.grn |
-  ${PBM_TESTPREFIX}giftopnm > ${tmpdir}/out.grn &&
-${PBM_TESTPREFIX}pamtogif ${tmpdir}/testimg.blu | \
-  ${PBM_TESTPREFIX}giftopnm | \
-  ${PBM_BINPREFIX}rgb3toppm ${tmpdir}/testimg.red ${tmpdir}/testimg.grn - | \
+ppmtorgb3 ${tmpdir}/testimg.ppm &&
+pamtogif ${tmpdir}/testimg.red | \
+  giftopnm > ${tmpdir}/out.red &&
+pamtogif ${tmpdir}/testimg.grn |
+  giftopnm > ${tmpdir}/out.grn &&
+pamtogif ${tmpdir}/testimg.blu | \
+  giftopnm | \
+  rgb3toppm ${tmpdir}/testimg.red ${tmpdir}/testimg.grn - | \
   cksum
 
 rm ${tmpdir}/testimg.{ppm,red,grn,blu} ${tmpdir}/out.{red,grn}
 
-# Test 3. Should produce 2425386270 41
-${PBM_TESTPREFIX}pamtogif testgrid.pbm | ${PBM_TESTPREFIX}giftopnm | cksum
+# Test 3. Should produce 2425386270 41 five times.
+
+pamtogif testgrid.pbm | giftopnm | cksum
+pamtogif -nolzw testgrid.pbm | giftopnm | cksum
+pamtogif -transparent=black testgrid.pbm | giftopnm | cksum
+pamtogif -alpha=testgrid.pbm testgrid.pbm | giftopnm | cksum
+pamtogif -transparent=white testgrid.pbm | giftopnm -alpha=- | \
+  pnminvert | cksum
 
 # Test 4.
 # In this gif file the code length changes after the last image data.
 # Image data: 3 bits, end code 4 bits.
 # Should produce P1 4 1 0 1 0 1
 
-${PBM_BINPREFIX}pbmmake -g 4 1 | \
-  ${PBM_TESTPREFIX}pamtogif | ${PBM_TESTPREFIX}giftopnm -plain | \
+pbmmake -g 4 1 | \
+  pamtogif | giftopnm -plain | \
   tr '\n' ' '
diff --git a/test/hdiff-roundtrip.test b/test/hdiff-roundtrip.test
index dbe74701..3a27a955 100755
--- a/test/hdiff-roundtrip.test
+++ b/test/hdiff-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: hdifftopam pamtohdiff
 # Also requires: 
 
-${PBM_TESTPREFIX}pamtohdiff testimg.ppm | \
-  ${PBM_TESTPREFIX}hdifftopam -pnm | cksum
\ No newline at end of file
+  alias hdifftopam="${PBM_TESTPREFIX}hdifftopam"
+  alias pamtohdiff="${PBM_TESTPREFIX}pamtohdiff"
+  shopt -s expand_aliases
+
+pamtohdiff testimg.ppm | \
+  hdifftopam -pnm | cksum
diff --git a/test/jbig-roundtrip.ok b/test/jbig-roundtrip.ok
new file mode 100644
index 00000000..a951db19
--- /dev/null
+++ b/test/jbig-roundtrip.ok
@@ -0,0 +1,2 @@
+2425386270 41
+2871603838 33838
diff --git a/test/jbig-roundtrip.test b/test/jbig-roundtrip.test
new file mode 100755
index 00000000..766148c6
--- /dev/null
+++ b/test/jbig-roundtrip.test
@@ -0,0 +1,14 @@
+#! /bin/bash
+# This script tests: pnmtojbig jbigtopnm
+# Also requires: ppmtopgm
+
+  alias pnmtojbig="${PBM_TESTPREFIX}pnmtojbig"
+  alias jbigtopnm="${PBM_TESTPREFIX}jbigtopnm"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
+# Test 1.  Should print 2425386270 41
+pnmtojbig testgrid.pbm | jbigtopnm | cksum
+
+# Test 2.  Should print 2871603838 33838
+ppmtopgm testimg.ppm | pnmtojbig | jbigtopnm | cksum
\ No newline at end of file
diff --git a/test/leaf-roundtrip.test b/test/leaf-roundtrip.test
index 6fb7547c..f8ac11d4 100755
--- a/test/leaf-roundtrip.test
+++ b/test/leaf-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: ppmtoleaf leaftoppm
 # Also requires: 
 
+  alias leaftoppm="${PBM_TESTPREFIX}leaftoppm"
+  alias ppmtoleaf="${PBM_TESTPREFIX}ppmtoleaf"
+  shopt -s expand_aliases
+
 # Should produce 1926073387 101484, cksum of testimg.ppm
-${PBM_TESTPREFIX}ppmtoleaf testimg.ppm | ${PBM_TESTPREFIX}leaftoppm | cksum
+ppmtoleaf testimg.ppm | leaftoppm | cksum
diff --git a/test/mgr-roundtrip.test b/test/mgr-roundtrip.test
index 2ca3d4b3..4f5fdd81 100755
--- a/test/mgr-roundtrip.test
+++ b/test/mgr-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtomgr mgrtopbm
 # Also requires: 
 
+  alias mgrtopbm="${PBM_TESTPREFIX}mgrtopbm"
+  alias pbmtomgr="${PBM_TESTPREFIX}pbmtomgr"
+  shopt -s expand_aliases
+
 # Should print 2425386270 41, cksum of testgrid.pbm
-${PBM_TESTPREFIX}pbmtomgr testgrid.pbm | ${PBM_TESTPREFIX}mgrtopbm | cksum
+pbmtomgr testgrid.pbm | mgrtopbm | cksum
diff --git a/test/mrf-roundtrip.test b/test/mrf-roundtrip.test
index 80dca546..5b9f77f4 100755
--- a/test/mrf-roundtrip.test
+++ b/test/mrf-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtomrf mrftopbm
 # Also requires: 
 
+  alias mrftopbm="${PBM_TESTPREFIX}mrftopbm"
+  alias pbmtomrf="${PBM_TESTPREFIX}pbmtomrf"
+  shopt -s expand_aliases
+
 # Should print 2425386270 41, cksum of testgrid.pbm
-${PBM_TESTPREFIX}pbmtomrf testgrid.pbm | ${PBM_TESTPREFIX}mrftopbm | cksum
+pbmtomrf testgrid.pbm | mrftopbm | cksum
diff --git a/test/pad-crop-roundtrip.test b/test/pad-crop-roundtrip.test
index 2b3c777b..ae19f7d5 100755
--- a/test/pad-crop-roundtrip.test
+++ b/test/pad-crop-roundtrip.test
@@ -2,7 +2,11 @@
 # This script tests: pnmcrop pnmmargin
 # Also requires: pnmpad
 
-${PBM_TESTPREFIX}pnmmargin -white 10 testimg.ppm | \
-  ${PBM_TESTPREFIX}pnmcrop | cksum
-${PBM_TESTPREFIX}pnmmargin -white 10 testgrid.pbm | \
-  ${PBM_TESTPREFIX}pnmcrop | cksum
+  alias pnmcrop="${PBM_TESTPREFIX}pnmcrop"
+  alias pnmmargin="${PBM_TESTPREFIX}pnmmargin"
+  shopt -s expand_aliases
+
+pnmmargin -white 10 testimg.ppm | \
+  pnmcrop | cksum
+pnmmargin -white 10 testgrid.pbm | \
+  pnmcrop | cksum
diff --git a/test/pamchannel.test b/test/pamchannel.test
index f83dd3b6..e3da552c 100755
--- a/test/pamchannel.test
+++ b/test/pamchannel.test
@@ -2,6 +2,10 @@
 # This script tests: pamchannel
 # Also requires: pamtopnm
 
+  alias pamchannel="${PBM_TESTPREFIX}pamchannel"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  shopt -s expand_aliases
+
 
 # Extract planes one by one.
 # Convert output to pgm to make it identical to ppmtorgb3 output.
@@ -16,17 +20,17 @@
 # Test 1. red channel
 # Should produce 1571496937 33838
 
-${PBM_TESTPREFIX}pamchannel -infile testimg.ppm 0 | \
-  ${PBM_BINPREFIX}pamtopnm --assume | cksum
+pamchannel -infile testimg.ppm 0 | \
+  pamtopnm --assume | cksum
 
 # Test 2. green channel
 # Should produce  394856971 33838
 
-${PBM_TESTPREFIX}pamchannel -infile testimg.ppm 1 | \
-  ${PBM_BINPREFIX}pamtopnm --assume | cksum
+pamchannel -infile testimg.ppm 1 | \
+  pamtopnm --assume | cksum
 
 # Test 3. blue channel
 # Should produce 3164158573 33838
 
-${PBM_TESTPREFIX}pamchannel -infile testimg.ppm 2 | \
-  ${PBM_BINPREFIX}pamtopnm --assume | cksum
+pamchannel -infile testimg.ppm 2 | \
+  pamtopnm --assume | cksum
diff --git a/test/pamcut.test b/test/pamcut.test
index 080bcad8..5a94de46 100755
--- a/test/pamcut.test
+++ b/test/pamcut.test
@@ -2,13 +2,17 @@
 # This script tests: pamcut pbmmake
 # Also requires: 
 
+  alias pamcut="${PBM_TESTPREFIX}pamcut"
+  alias pbmmake="${PBM_TESTPREFIX}pbmmake"
+  shopt -s expand_aliases
+
 # Test 1.  Should print 2958909756 124815
-${PBM_TESTPREFIX}pamcut -top 0 -left 0 -width 260 -height 160 \
+pamcut -top 0 -left 0 -width 260 -height 160 \
   -pad testimg.ppm | cksum
 # Test 2.  Should print 1550940962 10933
-${PBM_TESTPREFIX}pamcut -top 200 -left 120 -width 40 -height 40 \
+pamcut -top 200 -left 120 -width 40 -height 40 \
   -pad testimg.ppm | cksum
 # Test 3.  Should print 708474423 14
-${PBM_TESTPREFIX}pamcut -top 5 -left 5 -bottom 5 -right 5 testimg.ppm | cksum
+pamcut -top 5 -left 5 -bottom 5 -right 5 testimg.ppm | cksum
 # Test 3.  Should print 3412257956 129
-${PBM_TESTPREFIX}pbmmake -g 50 50 | ${PBM_TESTPREFIX}pamcut 5 5 30 30 | cksum
+pbmmake -g 50 50 | pamcut 5 5 30 30 | cksum
diff --git a/test/pamdepth-roundtrip.test b/test/pamdepth-roundtrip.test
index 2795d659..6330aaf9 100755
--- a/test/pamdepth-roundtrip.test
+++ b/test/pamdepth-roundtrip.test
@@ -2,11 +2,15 @@
 # This script tests: pamdepth pgmtopbm
 # Also requires: 
 
+  alias pamdepth="${PBM_TESTPREFIX}pamdepth"
+  alias pgmtopbm="${PBM_TESTPREFIX}pgmtopbm"
+  shopt -s expand_aliases
+
 for i in 300 500 1023 4095 5000 16383 65535
 do
-${PBM_TESTPREFIX}pamdepth $i testimg.ppm | \
-  ${PBM_TESTPREFIX}pamdepth 255 | cksum
+pamdepth $i testimg.ppm | \
+  pamdepth 255 | cksum
 done
 
-${PBM_TESTPREFIX}pamdepth 255 testgrid.pbm | ${PBM_TESTPREFIX}pamdepth 1 | \
-  ${PBM_TESTPREFIX}pgmtopbm -th -val=0.5 | cksum
+pamdepth 255 testgrid.pbm | pamdepth 1 | \
+  pgmtopbm -th -val=0.5 | cksum
diff --git a/test/pamdice-roundtrip.test b/test/pamdice-roundtrip.test
index 06fef8b2..7319249a 100755
--- a/test/pamdice-roundtrip.test
+++ b/test/pamdice-roundtrip.test
@@ -2,8 +2,12 @@
 # This script tests: pamdice pamundice
 # Also requires: 
 
-${PBM_TESTPREFIX}pamdice testimg.ppm -outstem=${tmpdir}/a -width=50 -height=40
-${PBM_TESTPREFIX}pamundice ${tmpdir}/a_%1d_%1a.ppm -down=4 -across=5 | cksum
+  alias pamdice="${PBM_TESTPREFIX}pamdice"
+  alias pamundice="${PBM_TESTPREFIX}pamundice"
+  shopt -s expand_aliases
+
+pamdice testimg.ppm -outstem=${tmpdir}/a -width=50 -height=40
+pamundice ${tmpdir}/a_%1d_%1a.ppm -down=4 -across=5 | cksum
 
 rm ${tmpdir}/a_?_?.ppm
 
diff --git a/test/pamditherbw.test b/test/pamditherbw.test
index 5335f12d..fef71efa 100755
--- a/test/pamditherbw.test
+++ b/test/pamditherbw.test
@@ -2,30 +2,34 @@
 # This script tests: pamditherbw
 # Also requires: ppmtopgm
 
+  alias pamditherbw="${PBM_TESTPREFIX}pamditherbw"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Make test input
-${PBM_BINPREFIX}ppmtopgm testimg.ppm >${tmpdir}/testimg.pgm
+ppmtopgm testimg.ppm >${tmpdir}/testimg.pgm
 
 # Test 1.  Simple threshold
-${PBM_TESTPREFIX}pamditherbw -threshold -val=0.5 \
+pamditherbw -threshold -val=0.5 \
    ${tmpdir}/testimg.pgm | cksum
 
 # Test 2.  Floyd-Steinberg
-#${PBM_TESTPREFIX}pamditherbw -floyd -val=0.5 ${tmpdir}/testimg.pgm | cksum
+#pamditherbw -floyd -val=0.5 ${tmpdir}/testimg.pgm | cksum
 
 # Test 3. Atkinson
-#${PBM_TESTPREFIX}pamditherbw -atkinson -val=0.5 ${tmpdir}/testimg.pgm | cksum
+#pamditherbw -atkinson -val=0.5 ${tmpdir}/testimg.pgm | cksum
 
 # Test 4. Hilbert
-${PBM_TESTPREFIX}pamditherbw -hilbert ${tmpdir}/testimg.pgm | cksum
+pamditherbw -hilbert ${tmpdir}/testimg.pgm | cksum
 
 # Test 5. Dither-8
-${PBM_TESTPREFIX}pamditherbw -dither8 ${tmpdir}/testimg.pgm | cksum
+pamditherbw -dither8 ${tmpdir}/testimg.pgm | cksum
 
 # Test 6. Cluster4
-${PBM_TESTPREFIX}pamditherbw -cluster4 ${tmpdir}/testimg.pgm | cksum
+pamditherbw -cluster4 ${tmpdir}/testimg.pgm | cksum
 
 # Test 7. Atkinson
-#${PBM_TESTPREFIX}pamditherbw -atkinson -val=0.5 ${tmpdir}/testimg.pgm | cksum
+#pamditherbw -atkinson -val=0.5 ${tmpdir}/testimg.pgm | cksum
 
 # Remove test file
 rm ${tmpdir}/testimg.pgm
diff --git a/test/pamedge.test b/test/pamedge.test
index da1c0145..d230424b 100755
--- a/test/pamedge.test
+++ b/test/pamedge.test
@@ -2,6 +2,14 @@
 # This script tests: pamedge
 # Also requires: pbmpscale pbmtext pgmtopbm pgmtopgm ppmtopgm
 
-${PBM_BINPREFIX}pbmtext " F " -nom | ${PBM_BINPREFIX}pbmpscale 5 | \
-${PBM_BINPREFIX}pgmtopgm | ${PBM_TESTPREFIX}pamedge | \
-${PBM_BINPREFIX}ppmtopgm | ${PBM_BINPREFIX}pgmtopbm -th -val=.5 -plain
+  alias pamedge="${PBM_TESTPREFIX}pamedge"
+  alias pbmpscale="${PBM_BINPREFIX}pbmpscale"
+  alias pbmtext="${PBM_BINPREFIX}pbmtext"
+  alias pgmtopbm="${PBM_BINPREFIX}pgmtopbm"
+  alias pgmtopgm="${PBM_BINPREFIX}pgmtopgm"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
+pbmtext " F " -nom | pbmpscale 5 | \
+pgmtopgm | pamedge | \
+ppmtopgm | pgmtopbm -th -val=.5 -plain
diff --git a/test/pamenlarge.test b/test/pamenlarge.test
index c3efc4b2..c1958d14 100755
--- a/test/pamenlarge.test
+++ b/test/pamenlarge.test
@@ -2,11 +2,15 @@
 # This script tests: pamenlarge
 # Also requires: ppmtopgm
 
+  alias pamenlarge="${PBM_TESTPREFIX}pamenlarge"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Test 1.  Should print 3424505894 913236
-${PBM_TESTPREFIX}pamenlarge 3 testimg.ppm | cksum
+pamenlarge 3 testimg.ppm | cksum
 # Test 2.  Should print 4152147096 304422
-${PBM_BINPREFIX}ppmtopgm testimg.ppm | ${PBM_TESTPREFIX}pamenlarge 3 | cksum
+ppmtopgm testimg.ppm | pamenlarge 3 | cksum
 # Test 3.  Should print 3342398172 297
-${PBM_TESTPREFIX}pamenlarge 3 testgrid.pbm | cksum
+pamenlarge 3 testgrid.pbm | cksum
 # Test 4.  Should print 237488670 3133413
-${PBM_TESTPREFIX}pamenlarge 3 -plain testimg.ppm | cksum
+pamenlarge 3 -plain testimg.ppm | cksum
diff --git a/test/pamfile.test b/test/pamfile.test
index 36ee557c..689578f1 100755
--- a/test/pamfile.test
+++ b/test/pamfile.test
@@ -2,6 +2,10 @@
 # This script tests: pamfile
 # Also requires: ppmtopgm
 
-${PBM_TESTPREFIX}pamfile testimg.ppm
-${PBM_TESTPREFIX}pamfile testgrid.pbm
-${PBM_BINPREFIX}ppmtopgm testimg.ppm | ${PBM_TESTPREFIX}pamfile
+  alias pamfile="${PBM_TESTPREFIX}pamfile"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
+pamfile testimg.ppm
+pamfile testgrid.pbm
+ppmtopgm testimg.ppm | pamfile
diff --git a/test/pamflip-roundtrip.test b/test/pamflip-roundtrip.test
index 7a17359f..2b18eb8d 100755
--- a/test/pamflip-roundtrip.test
+++ b/test/pamflip-roundtrip.test
@@ -2,33 +2,36 @@
 # This script tests: pamflip
 # Also requires: 
 
-${PBM_TESTPREFIX}pamflip -lr testimg.ppm | ${PBM_TESTPREFIX}pamflip -lr | cksum
-${PBM_TESTPREFIX}pamflip -tb testimg.ppm | ${PBM_TESTPREFIX}pamflip -tb | cksum
-${PBM_TESTPREFIX}pamflip -r180 testimg.ppm | \
-  ${PBM_TESTPREFIX}pamflip -r180 | cksum
-${PBM_TESTPREFIX}pamflip -xy testimg.ppm | ${PBM_TESTPREFIX}pamflip -xy | cksum
-${PBM_TESTPREFIX}pamflip -r90 testimg.ppm | \
-  ${PBM_TESTPREFIX}pamflip -r90 | \
-  ${PBM_TESTPREFIX}pamflip -r90 | \
-  ${PBM_TESTPREFIX}pamflip -r90 | cksum
-${PBM_TESTPREFIX}pamflip -r270 testimg.ppm | \
-  ${PBM_TESTPREFIX}pamflip -r270 | \
-  ${PBM_TESTPREFIX}pamflip -r270 | \
-  ${PBM_TESTPREFIX}pamflip -r270 | cksum
+  alias pamflip="${PBM_TESTPREFIX}pamflip"
+  shopt -s expand_aliases
 
-${PBM_TESTPREFIX}pamflip -lr testgrid.pbm | \
-  ${PBM_TESTPREFIX}pamflip -lr | cksum
-${PBM_TESTPREFIX}pamflip -tb testgrid.pbm | \
-  ${PBM_TESTPREFIX}pamflip -tb | cksum
-${PBM_TESTPREFIX}pamflip -r180 testgrid.pbm | \
-  ${PBM_TESTPREFIX}pamflip -r180 | cksum
-${PBM_TESTPREFIX}pamflip -xy testgrid.pbm | \
-  ${PBM_TESTPREFIX}pamflip -xy | cksum
-${PBM_TESTPREFIX}pamflip -r90 testgrid.pbm | \
-  ${PBM_TESTPREFIX}pamflip -r90 | \
-  ${PBM_TESTPREFIX}pamflip -r90 | \
-  ${PBM_TESTPREFIX}pamflip -r90 | cksum
-${PBM_TESTPREFIX}pamflip -r270 testgrid.pbm | \
-  ${PBM_TESTPREFIX}pamflip -r270 | \
-  ${PBM_TESTPREFIX}pamflip -r270 | \
-  ${PBM_TESTPREFIX}pamflip -r270 | cksum
+pamflip -lr testimg.ppm | pamflip -lr | cksum
+pamflip -tb testimg.ppm | pamflip -tb | cksum
+pamflip -r180 testimg.ppm | \
+  pamflip -r180 | cksum
+pamflip -xy testimg.ppm | pamflip -xy | cksum
+pamflip -r90 testimg.ppm | \
+  pamflip -r90 | \
+  pamflip -r90 | \
+  pamflip -r90 | cksum
+pamflip -r270 testimg.ppm | \
+  pamflip -r270 | \
+  pamflip -r270 | \
+  pamflip -r270 | cksum
+
+pamflip -lr testgrid.pbm | \
+  pamflip -lr | cksum
+pamflip -tb testgrid.pbm | \
+  pamflip -tb | cksum
+pamflip -r180 testgrid.pbm | \
+  pamflip -r180 | cksum
+pamflip -xy testgrid.pbm | \
+  pamflip -xy | cksum
+pamflip -r90 testgrid.pbm | \
+  pamflip -r90 | \
+  pamflip -r90 | \
+  pamflip -r90 | cksum
+pamflip -r270 testgrid.pbm | \
+  pamflip -r270 | \
+  pamflip -r270 | \
+  pamflip -r270 | cksum
diff --git a/test/pamflip.test b/test/pamflip.test
index 59f8e639..4ea275bd 100755
--- a/test/pamflip.test
+++ b/test/pamflip.test
@@ -2,15 +2,18 @@
 # This script tests: pamflip
 # Also requires: 
 
+  alias pamflip="${PBM_TESTPREFIX}pamflip"
+  shopt -s expand_aliases
+
 # Test 1.  Should print 2116496681 101484
-${PBM_TESTPREFIX}pamflip -lr testimg.ppm | cksum 
+pamflip -lr testimg.ppm | cksum 
 # Test 2.  Should print 217037000 101484
-${PBM_TESTPREFIX}pamflip -cw testimg.ppm | cksum
+pamflip -cw testimg.ppm | cksum
 # Test 3.  Should print 2052917888 101484
-${PBM_TESTPREFIX}pamflip -tb testimg.ppm | cksum
+pamflip -tb testimg.ppm | cksum
 # Test 4.  Should print 3375384165 41
-${PBM_TESTPREFIX}pamflip -lr testgrid.pbm | cksum
+pamflip -lr testgrid.pbm | cksum
 # Test 5.  Should print 604323149 41
-${PBM_TESTPREFIX}pamflip -tb testgrid.pbm | cksum
+pamflip -tb testgrid.pbm | cksum
 # Test 6.  Should print 490797850 37
-${PBM_TESTPREFIX}pamflip -cw testgrid.pbm | cksum
+pamflip -cw testgrid.pbm | cksum
diff --git a/test/pamseq.test b/test/pamseq.test
index 1c6ae44b..acc5d1b1 100755
--- a/test/pamseq.test
+++ b/test/pamseq.test
@@ -2,4 +2,7 @@
 # This script tests: pamseq
 # Also requires: 
 
-${PBM_TESTPREFIX}pamseq 1 255 | cksum
+  alias pamseq="${PBM_TESTPREFIX}pamseq"
+  shopt -s expand_aliases
+
+pamseq 1 255 | cksum
diff --git a/test/pamslice-roundtrip.ok b/test/pamslice-roundtrip.ok
index f9fe0bb4..eae64745 100644
--- a/test/pamslice-roundtrip.ok
+++ b/test/pamslice-roundtrip.ok
@@ -1,2 +1,4 @@
 2425386270 41
-1926073387 101484
+914327477 4864
+914327477 4864
+914327477 4864
diff --git a/test/pamslice-roundtrip.test b/test/pamslice-roundtrip.test
index d7ca657d..edec0d26 100755
--- a/test/pamslice-roundtrip.test
+++ b/test/pamslice-roundtrip.test
@@ -1,7 +1,15 @@
 #! /bin/bash
-# This script tests: pamslice
-# Also requires: pnmtopnm pamtopnm
+# This script tests: pamslice pamdeinterlace
+# Also requires: pamcut pnmtopnm pamflip
 
+  alias pamslice="${PBM_TESTPREFIX}pamslice"
+  alias pamdeinterlace="${PBM_TESTPREFIX}pamdeinterlace"
+  alias pamcut="${PBM_BINPREFIX}pamcut"
+  alias pnmtopnm="${PBM_BINPREFIX}pnmtopnm"
+  alias pamflip="${PBM_BINPREFIX}pamflip"
+  shopt -s expand_aliases
+
+# Test 1.
 # Slice rows, one by one, out of testgrid.pbm.
 # Add header and reconstruct pbm image.
 # Note that in pamslice output 0 is white and 1 is black: opposite of PBM
@@ -9,21 +17,56 @@
 
 (echo "P1"
  echo "14 16"
- seq 0 15 | while read i; 
+ seq 0 15 | while read i;
      do
-     ${PBM_TESTPREFIX}pamslice -row=$i testgrid.pbm  | \
-      awk '{print $2}' | sed 'y/01/10/'; 
-     done ) |  ${PBM_BINPREFIX}pnmtopnm | cksum
+     pamslice -row=$i testgrid.pbm  | \
+      awk '{print $2}' | sed 'y/01/10/';
+     done ) |  pnmtopnm | cksum
 
-# Slice rows, one by one, out of testimg.ppm.
-# Add header and reconstruct pbm image.
-# Should print 1926073387 101484
+# Test 2.
+# Slice rows, one by one, out of ppm test image
+# We take a part out of testimg.ppm with pamcut for processing the
+# whole image takes much time.
+# Add header and reconstruct ppm image.
+# Should print 914327477 4864
+
+pamcut 50 50 49 33 testimg.ppm > ${tmpdir}/test4933.ppm
+
+(echo "P3"
+ echo "49 33"
+ echo "255"
+ seq 0 32 | while read i;
+     do
+     pamslice -row=$i ${tmpdir}/test4933.ppm | awk '{print $2, $3, $4}';
+     done ) | pnmtopnm | cksum
+
+# Same as above test 2, but take cols instead of rows.
+# Should print 914327477 4864
 
 (echo "P3"
- echo "227 149"
+ echo "33 49"
  echo "255"
- seq 0 148 | while read i; 
+ seq 0 48 | while read i;
      do
-     ${PBM_TESTPREFIX}pamslice -row=$i testimg.ppm  | awk '{print $2, $3, $4}'; 
-     done ) |  ${PBM_BINPREFIX}pnmtopnm | cksum
+     pamslice -col=$i ${tmpdir}/test4933.ppm | awk '{print $2, $3, $4}';
+     done ) | pamflip -xy | cksum
+
+# Test 4.
+# Divide input image into two with pamdeinterlace and recombine.
+
+pamdeinterlace -takeodd ${tmpdir}/test4933.ppm > ${tmpdir}/testodd.ppm
+pamdeinterlace -takeeven ${tmpdir}/test4933.ppm > ${tmpdir}/testevn.ppm
+
+(echo "P3"
+ echo "49 33"
+ echo "255"
+ seq 0 15 | while read i;
+     do
+     pamslice -row=$i ${tmpdir}/testevn.ppm | awk '{print $2, $3, $4}';
+     pamslice -row=$i ${tmpdir}/testodd.ppm | awk '{print $2, $3, $4}';
+     done
+     pamslice -row=16 ${tmpdir}/testevn.ppm | awk '{print $2, $3, $4}';
+  ) | pnmtopnm | tee /tmp/z | cksum
+
+rm ${tmpdir}/test4933.ppm ${tmpdir}/testodd.ppm ${tmpdir}/testevn.ppm
 
diff --git a/test/pamsumm.test b/test/pamsumm.test
index 9c5f1b84..9697a2b9 100755
--- a/test/pamsumm.test
+++ b/test/pamsumm.test
@@ -2,12 +2,15 @@
 # This script tests: pamsumm
 # Also requires: 
 
+  alias pamsumm="${PBM_TESTPREFIX}pamsumm"
+  shopt -s expand_aliases
+
 for type in -sum -min -max -mean
   do
-  ${PBM_TESTPREFIX}pamsumm -brief $type testgrid.pbm
+  pamsumm -brief $type testgrid.pbm
   done
 
 for type in -sum -min -max -mean
   do
-  ${PBM_TESTPREFIX}pamsumm -brief $type testimg.ppm
+  pamsumm -brief $type testimg.ppm
   done
diff --git a/test/pamtopam.test b/test/pamtopam.test
index 1a4cdff6..e2ec5adf 100755
--- a/test/pamtopam.test
+++ b/test/pamtopam.test
@@ -2,8 +2,11 @@
 # This script tests: pamtopam
 # Also requires: 
 
-${PBM_TESTPREFIX}pamtopam < testimg.ppm  | sed '/ENDHDR/q'
-${PBM_TESTPREFIX}pamtopam < testgrid.pbm | sed '/ENDHDR/q'
+  alias pamtopam="${PBM_TESTPREFIX}pamtopam"
+  shopt -s expand_aliases
 
-${PBM_TESTPREFIX}pamtopam < testimg.ppm   | cksum
-${PBM_TESTPREFIX}pamtopam < testgrid.pbm  | cksum
+pamtopam < testimg.ppm  | sed '/ENDHDR/q'
+pamtopam < testgrid.pbm | sed '/ENDHDR/q'
+
+pamtopam < testimg.ppm   | cksum
+pamtopam < testgrid.pbm  | cksum
diff --git a/test/pbmclean.test b/test/pbmclean.test
index 69081ff9..28242d30 100755
--- a/test/pbmclean.test
+++ b/test/pbmclean.test
@@ -2,15 +2,21 @@
 # This script tests: pbmclean
 # Also requires: pbmmake pbmpage pnmmargin pnmpad
 
-${PBM_BINPREFIX}pbmmake -g 3 3 | ${PBM_BINPREFIX}pnmmargin -black 2 \
+  alias pbmclean="${PBM_TESTPREFIX}pbmclean"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pbmpage="${PBM_BINPREFIX}pbmpage"
+  alias pnmmargin="${PBM_BINPREFIX}pnmmargin"
+  shopt -s expand_aliases
+
+pbmmake -g 3 3 | pnmmargin -black 2 \
  >${tmpdir}/test.pbm
 
 for n in 1 2 3 4 5 6 7 8
 do
-${PBM_TESTPREFIX}pbmclean -min=$n -black -plain ${tmpdir}/test.pbm
+pbmclean -min=$n -black -plain ${tmpdir}/test.pbm
 done
 
 rm ${tmpdir}/test.pbm
 
 # Should print 760076056 4210813
-${PBM_BINPREFIX}pbmpage 1 | ${PBM_TESTPREFIX}pbmclean -black | cksum
+pbmpage 1 | pbmclean -black | cksum
diff --git a/test/pbmmake.test b/test/pbmmake.test
index 82fdb941..eb4b472d 100755
--- a/test/pbmmake.test
+++ b/test/pbmmake.test
@@ -2,17 +2,20 @@
 # This script tests: pbmmake
 # Also requires: 
 
+  alias pbmmake="${PBM_TESTPREFIX}pbmmake"
+  shopt -s expand_aliases
+
 for i in `seq 1 8`
 do
 for color in -w -b -g
 do
-${PBM_TESTPREFIX}pbmmake -plain $color $i $i | tr -d '\n'; echo
+pbmmake -plain $color $i $i | tr -d '\n'; echo
 done
 done
 for i in `seq 8 5 98`
 do
- ( ${PBM_TESTPREFIX}pbmmake -w $i $i ;
-  ${PBM_TESTPREFIX}pbmmake -b $i $i ;
-  ${PBM_TESTPREFIX}pbmmake  -g $i $i ) | cksum
+ ( pbmmake -w $i $i ;
+  pbmmake -b $i $i ;
+  pbmmake  -g $i $i ) | cksum
 done
 
diff --git a/test/pbmminkowski.test b/test/pbmminkowski.test
index ccb882b1..120441f1 100755
--- a/test/pbmminkowski.test
+++ b/test/pbmminkowski.test
@@ -2,9 +2,14 @@
 # This script tests: pbmminkowski
 # Also requires: pbmmake pnmmargin pnmpad
 
-${PBM_BINPREFIX}pbmmake -w 1 1 | ${PBM_BINPREFIX}pnmmargin -b 1 | \
-  ${PBM_TESTPREFIX}pbmminkowski
+  alias pbmminkowski="${PBM_TESTPREFIX}pbmminkowski"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pnmmargin="${PBM_BINPREFIX}pnmmargin"
+  shopt -s expand_aliases
+
+pbmmake -w 1 1 | pnmmargin -b 1 | \
+  pbmminkowski
 echo
-${PBM_BINPREFIX}pbmmake -g 3 3 | ${PBM_TESTPREFIX}pbmminkowski
+pbmmake -g 3 3 | pbmminkowski
 echo
-${PBM_TESTPREFIX}pbmminkowski testgrid.pbm
\ No newline at end of file
+pbmminkowski testgrid.pbm
diff --git a/test/pbmpage.test b/test/pbmpage.test
index dcf8e688..c03ee8d7 100755
--- a/test/pbmpage.test
+++ b/test/pbmpage.test
@@ -2,6 +2,9 @@
 # This script tests: pbmpage 
 # Also requires: 
 
-${PBM_TESTPREFIX}pbmpage 1 | cksum
-${PBM_TESTPREFIX}pbmpage 2 | cksum
-${PBM_TESTPREFIX}pbmpage 3 | cksum
\ No newline at end of file
+  alias pbmpage="${PBM_TESTPREFIX}pbmpage"
+  shopt -s expand_aliases
+
+pbmpage 1 | cksum
+pbmpage 2 | cksum
+pbmpage 3 | cksum
diff --git a/test/pbmpscale.test b/test/pbmpscale.test
index e1125fc7..50ae470d 100755
--- a/test/pbmpscale.test
+++ b/test/pbmpscale.test
@@ -2,10 +2,15 @@
 # This script tests: pbmpscale
 # Also requires: pamenlarge pbmtext
 
-${PBM_BINPREFIX}pbmtext -nomargin "F" | ${PBM_TESTPREFIX}pbmpscale 3 -plain 
+  alias pbmpscale="${PBM_TESTPREFIX}pbmpscale"
+  alias pamenlarge="${PBM_BINPREFIX}pamenlarge"
+  alias pbmtext="${PBM_BINPREFIX}pbmtext"
+  shopt -s expand_aliases
+
+pbmtext -nomargin "F" | pbmpscale 3 -plain 
 
 for i in 2 3 4
 do
-${PBM_BINPREFIX}pamenlarge 2 testgrid.pbm | \
-  ${PBM_TESTPREFIX}pbmpscale $i | cksum
+pamenlarge 2 testgrid.pbm | \
+  pbmpscale $i | cksum
 done
diff --git a/test/pbmtext.test b/test/pbmtext.test
index 69fa9b23..21020969 100755
--- a/test/pbmtext.test
+++ b/test/pbmtext.test
@@ -2,10 +2,13 @@
 # This script tests: pbmtext
 # Also requires: 
 
+  alias pbmtext="${PBM_TESTPREFIX}pbmtext"
+  shopt -s expand_aliases
+
 for i in 0123456789 abcdefghijk lmnopqrst uzwxyz ABCDEFGHIJK LMNOPQRST UVWXYZ
 do
 for flags in "" "-nom" "-builtin fixed"
 do
-echo $i | ${PBM_TESTPREFIX}pbmtext $flags | cksum
+echo $i | pbmtext $flags | cksum
 done
 done
diff --git a/test/pbmtog3.test b/test/pbmtog3.test
index 09b5d784..74b901e3 100755
--- a/test/pbmtog3.test
+++ b/test/pbmtog3.test
@@ -2,27 +2,31 @@
 # This script tests: pbmtog3
 # Also requires: pbmmake
 
+  alias pbmtog3="${PBM_TESTPREFIX}pbmtog3"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  shopt -s expand_aliases
+
 # Test 1.  Should print 3697098186 144
-${PBM_TESTPREFIX}pbmtog3 testgrid.pbm | cksum
+pbmtog3 testgrid.pbm | cksum
 # Test 2.  Should print 1248301383 122
-${PBM_TESTPREFIX}pbmtog3 -nofixedwidth testgrid.pbm | cksum
+pbmtog3 -nofixedwidth testgrid.pbm | cksum
 # Test 3.  Should print 686713716 144
-${PBM_TESTPREFIX}pbmtog3 -reverse testgrid.pbm | cksum
+pbmtog3 -reverse testgrid.pbm | cksum
 # Test 4.  Should print 215463240 122
-${PBM_TESTPREFIX}pbmtog3 -nofixedwidth -reverse testgrid.pbm | cksum
+pbmtog3 -nofixedwidth -reverse testgrid.pbm | cksum
 # Test 5.  Should print 28792587 47
-${PBM_BINPREFIX}pbmmake -w 10 10 | ${PBM_TESTPREFIX}pbmtog3 | cksum
+pbmmake -w 10 10 | pbmtog3 | cksum
 # Test 6.  Should print 277456854 32
-${PBM_BINPREFIX}pbmmake -w 10 10 | \
-  ${PBM_TESTPREFIX}pbmtog3 -nofixedwidth | cksum
+pbmmake -w 10 10 | \
+  pbmtog3 -nofixedwidth | cksum
 # Test 7.  Should print 28792587 47
-${PBM_BINPREFIX}pbmmake -w 10000 10 | ${PBM_TESTPREFIX}pbmtog3 | cksum
+pbmmake -w 10000 10 | pbmtog3 | cksum
 # Test 8.  Should print 871281767 162
-${PBM_BINPREFIX}pbmmake -w 10000 10 | \
-  ${PBM_TESTPREFIX}pbmtog3 -nofixedwidth | cksum
+pbmmake -w 10000 10 | \
+  pbmtog3 -nofixedwidth | cksum
 # Test 9.  Should print 3736247115 62
-${PBM_BINPREFIX}pbmmake -b 10 10 | ${PBM_TESTPREFIX}pbmtog3 | cksum
+pbmmake -b 10 10 | pbmtog3 | cksum
 # Test 10.  Should print 2820255307 2191856
-${PBM_BINPREFIX}pbmmake -g 1700 2286 | ${PBM_TESTPREFIX}pbmtog3 | cksum
+pbmmake -g 1700 2286 | pbmtog3 | cksum
 # Test 11.  Should print 4159089282 2226575
-${PBM_BINPREFIX}pbmmake -g 1800 2286 | ${PBM_TESTPREFIX}pbmtog3 | cksum
+pbmmake -g 1800 2286 | pbmtog3 | cksum
diff --git a/test/pbmupc.test b/test/pbmupc.test
index 6fa772d7..39e4c10c 100755
--- a/test/pbmupc.test
+++ b/test/pbmupc.test
@@ -2,7 +2,10 @@
 # This script tests: pbmupc
 # Also requires: 
 
+  alias pbmupc="${PBM_TESTPREFIX}pbmupc"
+  shopt -s expand_aliases
+
 for type in -s1 -s2
 do
-${PBM_TESTPREFIX}pbmupc $type 0 72890 00011
+pbmupc $type 0 72890 00011
 done | cksum
diff --git a/test/pfm-roundtrip.test b/test/pfm-roundtrip.test
index 7cd2693c..650cc4d0 100755
--- a/test/pfm-roundtrip.test
+++ b/test/pfm-roundtrip.test
@@ -2,6 +2,11 @@
 # This script tests: pamtopfm pfmtopam
 # Also requires: pamtopnm
 
+  alias pamtopfm="${PBM_TESTPREFIX}pamtopfm"
+  alias pfmtopam="${PBM_TESTPREFIX}pfmtopam"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  shopt -s expand_aliases
+
 # Should print 1926073387 101484, cksum of testimg.ppm
-${PBM_TESTPREFIX}pamtopfm testimg.ppm | ${PBM_TESTPREFIX}pfmtopam | \
-  ${PBM_BINPREFIX}pamtopnm | cksum
+pamtopfm testimg.ppm | pfmtopam | \
+  pamtopnm | cksum
diff --git a/test/pgmcrater.test b/test/pgmcrater.test
index e6942cee..ffba435f 100755
--- a/test/pgmcrater.test
+++ b/test/pgmcrater.test
@@ -1,6 +1,9 @@
 #! /bin/bash
 # This script tests: pgmcrater
 
+  alias pgmcrater="${PBM_TESTPREFIX}pgmcrater"
+  shopt -s expand_aliases
+
 # This test is sensitive to differences in floating point math.
 # With GCC slight results start to appear with -number 75 in
 # the following test.  Compiler type (GCC, Clang, etc.),
@@ -17,7 +20,7 @@ testrandom -q
 case $? in
    81)
       # Should print: 3828822912 65551
-      ${PBM_TESTPREFIX}pgmcrater -quiet -number 25 -randomseed 1 | cksum
+      pgmcrater -quiet -number 25 -randomseed 1 | cksum
       ;;
 
    8[02-9] | 90)
diff --git a/test/pgmhist.test b/test/pgmhist.test
index b3361dc4..32b18189 100755
--- a/test/pgmhist.test
+++ b/test/pgmhist.test
@@ -2,10 +2,14 @@
 # This script tests: pgmhist
 # Also requires: pgmramp
 
+  alias pgmhist="${PBM_TESTPREFIX}pgmhist"
+  alias pgmramp="${PBM_BINPREFIX}pgmramp"
+  shopt -s expand_aliases
+
 # Ignore differences in spaces.
 
-${PBM_BINPREFIX}pgmramp -maxval=8 -lr 8 2 | ${PBM_TESTPREFIX}pgmhist | \
+pgmramp -maxval=8 -lr 8 2 | pgmhist | \
   sed -e 's/  */ /g' -e 's/ *$//'
 
-${PBM_TESTPREFIX}pgmhist testgrid.pbm | \
-  sed -e 's/  */ /g' -e 's/ *$//'
\ No newline at end of file
+pgmhist testgrid.pbm | \
+  sed -e 's/  */ /g' -e 's/ *$//'
diff --git a/test/pgmmake.test b/test/pgmmake.test
index 0180b502..2c384e1b 100755
--- a/test/pgmmake.test
+++ b/test/pgmmake.test
@@ -2,9 +2,12 @@
 # This script tests: pgmmake
 # Also requires: 
 
-${PBM_TESTPREFIX}pgmmake 1 50 50 | cksum
-${PBM_TESTPREFIX}pgmmake .2 50 100 -maxval=5 | cksum 
+  alias pgmmake="${PBM_TESTPREFIX}pgmmake"
+  shopt -s expand_aliases
 
+pgmmake 1 50 50 | cksum
+pgmmake .2 50 100 -maxval=5 | cksum 
 
 
- 
\ No newline at end of file
+
+ 
diff --git a/test/pgmnoise.test b/test/pgmnoise.test
index 60d06510..a5c1c380 100755
--- a/test/pgmnoise.test
+++ b/test/pgmnoise.test
@@ -2,6 +2,9 @@
 # This script tests: pgmnoise
 # Also requires: 
 
+  alias pgmnoise="${PBM_TESTPREFIX}pgmnoise"
+  shopt -s expand_aliases
+
 # We first check whether random number generator is glibc rand().
 # If not, this test is skipped.
 
@@ -10,7 +13,7 @@ testrandom
 case $? in
    81)
         # Should print: 1663614689 10015
-        ${PBM_TESTPREFIX}pgmnoise --randomseed=0 100 100 | cksum ;;
+        pgmnoise --randomseed=0 100 100 | cksum ;;
 
         # Any additional tests go here.
 
diff --git a/test/pgmramp.ok b/test/pgmramp.ok
index 0971ccd7..989ef7d4 100644
--- a/test/pgmramp.ok
+++ b/test/pgmramp.ok
@@ -1,32 +1,40 @@
 P2
 4 4
-3
-0 1 2 3
-0 1 2 3
-0 1 2 3
-0 1 2 3
+6
+0 2 4 6
+0 2 4 6
+0 2 4 6
+0 2 4 6
 P2
 4 4
-3
+6
 0 0 0 0
-1 1 1 1
 2 2 2 2
-3 3 3 3
+4 4 4 4
+6 6 6 6
 P2
 4 4
-3
-0 0 1 0
-0 1 2 1
-1 2 3 2
-0 1 2 1
+6
+0 1 3 1
+1 3 4 3
+3 4 6 4
+1 3 4 3
 P2
 4 4
-3
+6
 0 0 0 0
-0 1 2 1
-0 2 3 2
-0 1 2 1
+0 3 4 3
+0 4 6 4
+0 3 4 3
+P2
+4 4
+6
+0 1 2 3
+1 2 3 4
+2 3 4 5
+3 4 5 6
 1777787286 65551
 2046889993 65551
 1975520432 65551
 807973067 65551
+886972785 131087
diff --git a/test/pgmramp.test b/test/pgmramp.test
index cf3d91d7..231b9311 100755
--- a/test/pgmramp.test
+++ b/test/pgmramp.test
@@ -2,11 +2,17 @@
 # This script tests: pgmramp
 # Also requires: 
 
-for type in -lr -tb -rect -ell
+  alias pgmramp="${PBM_TESTPREFIX}pgmramp"
+  shopt -s expand_aliases
+
+for type in -lr -tb -rectangle -ellipse -diagonal 
 do
-${PBM_TESTPREFIX}pgmramp -maxval=3 $type 4 4 -plain
+pgmramp -maxval=6 $type 4 4 -plain
 done
 
-for type in -lr -tb -rect -ell
-do ${PBM_TESTPREFIX}pgmramp $type 256 256 | cksum
+for type in -lr -tb -rectangle -ellipse
+do pgmramp $type 256 256 | cksum
 done
+
+pgmramp -diagonal -maxval=510 256 256 | cksum
+  
\ No newline at end of file
diff --git a/test/pgmtopgm.test b/test/pgmtopgm.test
index 638cb68b..388cca93 100755
--- a/test/pgmtopgm.test
+++ b/test/pgmtopgm.test
@@ -2,4 +2,7 @@
 # This script tests: pgmtopgm
 # Also requires: 
 
-${PBM_TESTPREFIX}pgmtopgm < testgrid.pbm | cksum
+  alias pgmtopgm="${PBM_TESTPREFIX}pgmtopgm"
+  shopt -s expand_aliases
+
+pgmtopgm < testgrid.pbm | cksum
diff --git a/test/pgmtoppm.test b/test/pgmtoppm.test
index b93d7867..cdd53623 100755
--- a/test/pgmtoppm.test
+++ b/test/pgmtoppm.test
@@ -2,16 +2,22 @@
 # This script tests: pgmtoppm
 # Also requires: pamseq pamtopnm pgmramp
 
-${PBM_BINPREFIX}pgmramp -maxval=5 -lr 256 1 >${tmpdir}/test.pgm
-${PBM_BINPREFIX}pamseq 3 5 -tupletype=RGB | ${PBM_BINPREFIX}pamtopnm \
+  alias pgmtoppm="${PBM_TESTPREFIX}pgmtoppm"
+  alias pamseq="${PBM_BINPREFIX}pamseq"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  alias pgmramp="${PBM_BINPREFIX}pgmramp"
+  shopt -s expand_aliases
+
+pgmramp -maxval=5 -lr 256 1 >${tmpdir}/test.pgm
+pamseq 3 5 -tupletype=RGB | pamtopnm \
   >${tmpdir}/palette
 
 # Test 1.
-${PBM_TESTPREFIX}pgmtoppm green ${tmpdir}/test.pgm | cksum
+pgmtoppm green ${tmpdir}/test.pgm | cksum
 
-${PBM_TESTPREFIX}pgmtoppm yellow-blue ${tmpdir}/test.pgm | cksum
+pgmtoppm yellow-blue ${tmpdir}/test.pgm | cksum
 
-${PBM_TESTPREFIX}pgmtoppm -map=${tmpdir}/palette ${tmpdir}/test.pgm | cksum
+pgmtoppm -map=${tmpdir}/palette ${tmpdir}/test.pgm | cksum
 
 rm ${tmpdir}/test.pgm ${tmpdir}/palette
 
diff --git a/test/png-roundtrip.ok b/test/png-roundtrip.ok
index 67f7a1fe..28b8c057 100644
--- a/test/png-roundtrip.ok
+++ b/test/png-roundtrip.ok
@@ -1,2 +1,36 @@
 1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
 2425386270 41
diff --git a/test/png-roundtrip.test b/test/png-roundtrip.test
index 6f25d1c3..223103c8 100755
--- a/test/png-roundtrip.test
+++ b/test/png-roundtrip.test
@@ -2,6 +2,52 @@
 # This script tests: pngtopnm pnmtopng
 # Also requires: 
 
-${PBM_TESTPREFIX}pnmtopng testimg.ppm | ${PBM_TESTPREFIX}pngtopnm | cksum
-${PBM_TESTPREFIX}pnmtopng testgrid.pbm | ${PBM_TESTPREFIX}pngtopnm | cksum
+  alias pngtopnm="${PBM_TESTPREFIX}pngtopnm"
+  alias pnmtopng="${PBM_TESTPREFIX}pnmtopng"
+  shopt -s expand_aliases
 
+## Fails because .ok not set yet.
+
+# Test 1.  Should print 1926073387 101484 18 times
+for flags in "" -interlace \
+  -gamma=.45 \
+  -hist \
+  -nofilter \
+  -sub \
+  -up \
+  -avg \
+  -paeth \
+  -compression=9 \
+  "-compression=0 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=15 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=0 -comp_mem=9 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=9 -comp_window=15 -comp_buffer=8096" \
+  -comp_strategy=huffman_only \
+  -comp_strategy=filtered \
+  -force
+  do
+pnmtopng testimg.ppm $flags | pngtopnm | cksum
+done
+
+# Test 2.  Should print 2425386270 41 18 times
+for flags in "" -interlace \
+  -gamma=.45 \
+  -hist \
+  -nofilter \
+  -sub \
+  -up \
+  -avg \
+  -paeth \
+  -compression=9 \
+  "-compression=0 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=15 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=0 -comp_mem=9 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=9 -comp_window=15 -comp_buffer=8096" \
+  -comp_strategy=huffman_only \
+  -comp_strategy=filtered \
+  -force
+  do
+  pnmtopng testgrid.pbm $flags | pngtopnm | cksum
+  done
diff --git a/test/pnm-pam-roundtrip.test b/test/pnm-pam-roundtrip.test
index 1aef986c..506d25aa 100755
--- a/test/pnm-pam-roundtrip.test
+++ b/test/pnm-pam-roundtrip.test
@@ -2,6 +2,10 @@
 # This script tests: pamtopam pamtopnm
 # Also requires: 
 
-${PBM_TESTPREFIX}pamtopam < testimg.ppm | ${PBM_TESTPREFIX}pamtopnm | cksum
-${PBM_TESTPREFIX}pamtopam < testgrid.pbm | ${PBM_TESTPREFIX}pamtopnm | cksum
+  alias pamtopam="${PBM_TESTPREFIX}pamtopam"
+  alias pamtopnm="${PBM_TESTPREFIX}pamtopnm"
+  shopt -s expand_aliases
+
+pamtopam < testimg.ppm | pamtopnm | cksum
+pamtopam < testgrid.pbm | pamtopnm | cksum
 
diff --git a/test/pnm-plain-roundtrip.test b/test/pnm-plain-roundtrip.test
index 86dbb2fb..7d0293ee 100755
--- a/test/pnm-plain-roundtrip.test
+++ b/test/pnm-plain-roundtrip.test
@@ -2,8 +2,11 @@
 # This script tests: pnmtopnm
 # Also requires: pamtopnm
 
-${PBM_TESTPREFIX}pnmtopnm -plain testimg.ppm | \
-  ${PBM_TESTPREFIX}pnmtopnm | cksum
-${PBM_TESTPREFIX}pnmtopnm -plain testgrid.pbm | \
-  ${PBM_TESTPREFIX}pnmtopnm | cksum
+  alias pnmtopnm="${PBM_TESTPREFIX}pnmtopnm"
+  shopt -s expand_aliases
+
+pnmtopnm -plain testimg.ppm | \
+  pnmtopnm | cksum
+pnmtopnm -plain testgrid.pbm | \
+  pnmtopnm | cksum
 
diff --git a/test/pnmcat.test b/test/pnmcat.test
index 35efd515..b77c90fd 100755
--- a/test/pnmcat.test
+++ b/test/pnmcat.test
@@ -2,8 +2,11 @@
 # This script tests: pnmcat
 # Also requires: 
 
-${PBM_TESTPREFIX}pnmcat -lr testgrid.pbm testgrid.pbm | cksum
-${PBM_TESTPREFIX}pnmcat -tb testgrid.pbm testgrid.pbm | cksum
+  alias pnmcat="${PBM_TESTPREFIX}pnmcat"
+  shopt -s expand_aliases
 
-${PBM_TESTPREFIX}pnmcat -lr testimg.ppm testimg.ppm | cksum
-${PBM_TESTPREFIX}pnmcat -tb testimg.ppm testimg.ppm | cksum
+pnmcat -lr testgrid.pbm testgrid.pbm | cksum
+pnmcat -tb testgrid.pbm testgrid.pbm | cksum
+
+pnmcat -lr testimg.ppm testimg.ppm | cksum
+pnmcat -tb testimg.ppm testimg.ppm | cksum
diff --git a/test/pnminvert-roundtrip.test b/test/pnminvert-roundtrip.test
index 8cdb6c25..eb3e2b35 100755
--- a/test/pnminvert-roundtrip.test
+++ b/test/pnminvert-roundtrip.test
@@ -2,5 +2,8 @@
 # This script tests: pnminvert
 # Also requires: 
 
-${PBM_TESTPREFIX}pnminvert testimg.ppm | ${PBM_TESTPREFIX}pnminvert | cksum
-${PBM_TESTPREFIX}pnminvert testgrid.pbm | ${PBM_TESTPREFIX}pnminvert | cksum
+  alias pnminvert="${PBM_TESTPREFIX}pnminvert"
+  shopt -s expand_aliases
+
+pnminvert testimg.ppm | pnminvert | cksum
+pnminvert testgrid.pbm | pnminvert | cksum
diff --git a/test/pnminvert.test b/test/pnminvert.test
index 7fc8523c..eb25d2a0 100755
--- a/test/pnminvert.test
+++ b/test/pnminvert.test
@@ -2,19 +2,24 @@
 # This script tests: pnminvert
 # Also requires: pbmmake ppmtopgm
 
+  alias pnminvert="${PBM_TESTPREFIX}pnminvert"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Test 1.  Should print 1240379484 41
-${PBM_TESTPREFIX}pnminvert testgrid.pbm | cksum
+pnminvert testgrid.pbm | cksum
 # Test 2.  Should print 1416115901 101484
-${PBM_TESTPREFIX}pnminvert testimg.ppm | cksum
+pnminvert testimg.ppm | cksum
 # Test 3.  Should print 2961441369 33838
 # printed 4215652354 33838 with older ppmtopgm
-${PBM_BINPREFIX}ppmtopgm testimg.ppm | ${PBM_TESTPREFIX}pnminvert | cksum
+ppmtopgm testimg.ppm | pnminvert | cksum
 # Test 4.  Should print 2595564405 14
-${PBM_BINPREFIX}pbmmake -w 7 7 | ${PBM_TESTPREFIX}pnminvert | cksum
+pbmmake -w 7 7 | pnminvert | cksum
 # Test 5.  Should print 2595564405 14
-${PBM_BINPREFIX}pbmmake -b 7 7 | cksum
+pbmmake -b 7 7 | cksum
 # Test 6.  Should print 2595564405 14
-${PBM_BINPREFIX}pbmmake -b 7 7 | ${PBM_TESTPREFIX}pnminvert | \
-  ${PBM_TESTPREFIX}pnminvert | cksum
+pbmmake -b 7 7 | pnminvert | \
+  pnminvert | cksum
 # Test 7.  Should print 2896726098 15
-${PBM_BINPREFIX}pbmmake -g 8 8 | ${PBM_TESTPREFIX}pnminvert | cksum
+pbmmake -g 8 8 | pnminvert | cksum
diff --git a/test/pnmpsnr.test b/test/pnmpsnr.test
index d5aa07f3..da88d70d 100755
--- a/test/pnmpsnr.test
+++ b/test/pnmpsnr.test
@@ -2,10 +2,14 @@
 # This script tests: pnmpsnr
 # Also requires: pbmmake
 
-${PBM_BINPREFIX}pbmmake -w 10 10 > ${tmpdir}/w.pbm
-${PBM_BINPREFIX}pbmmake -b 10 10 > ${tmpdir}/b.pbm
-${PBM_TESTPREFIX}pnmpsnr  ${tmpdir}/w.pbm  ${tmpdir}/b.pbm 2>&1 | \
+  alias pnmpsnr="${PBM_TESTPREFIX}pnmpsnr"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  shopt -s expand_aliases
+
+pbmmake -w 10 10 > ${tmpdir}/w.pbm
+pbmmake -b 10 10 > ${tmpdir}/b.pbm
+pnmpsnr  ${tmpdir}/w.pbm  ${tmpdir}/b.pbm 2>&1 | \
  awk '{print $(NF-1),$NF}'
-${PBM_TESTPREFIX}pnmpsnr  ${tmpdir}/w.pbm  ${tmpdir}/w.pbm 2>&1 | \
+pnmpsnr  ${tmpdir}/w.pbm  ${tmpdir}/w.pbm 2>&1 | \
  awk '{print $(NF-1),$NF}'
 rm ${tmpdir}/b.pbm ${tmpdir}/w.pbm
diff --git a/test/pnmremap1.test b/test/pnmremap1.test
index 1e68c8ad..208be2fb 100755
--- a/test/pnmremap1.test
+++ b/test/pnmremap1.test
@@ -2,9 +2,15 @@
 # This script tests: pnmremap
 # Also requires: pamdepth pamseq pamtopnm
 
-${PBM_BINPREFIX}pamseq 3 5 -tupletype=RGB | ${PBM_BINPREFIX}pamtopnm \
+  alias pnmremap="${PBM_TESTPREFIX}pnmremap"
+  alias pamdepth="${PBM_BINPREFIX}pamdepth"
+  alias pamseq="${PBM_BINPREFIX}pamseq"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  shopt -s expand_aliases
+
+pamseq 3 5 -tupletype=RGB | pamtopnm \
  > ${tmpdir}/palette
-${PBM_BINPREFIX}pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
+pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
 
 # Test 1. Floyd-Steinberg
 # This fails with older versions of Netpbm and x86-64.
@@ -13,7 +19,7 @@ ${PBM_BINPREFIX}pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
 # x86-32: 2667816854 101482 
 # x86-64: 3602410851 101482
 
-${PBM_TESTPREFIX}pnmremap -mapfile=${tmpdir}/palette -floyd -norandom \
+pnmremap -mapfile=${tmpdir}/palette -floyd -norandom \
  testimg.ppm | cksum
 
 rm ${tmpdir}/palette{,255}
diff --git a/test/pnmremap2.test b/test/pnmremap2.test
index f2fd92e0..3399dc39 100755
--- a/test/pnmremap2.test
+++ b/test/pnmremap2.test
@@ -2,20 +2,26 @@
 # This script tests: pnmremap
 # Also requires: pamdepth pamseq pamtopnm
 
-${PBM_BINPREFIX}pamseq 3 5 -tupletype=RGB | ${PBM_BINPREFIX}pamtopnm \
+  alias pnmremap="${PBM_TESTPREFIX}pnmremap"
+  alias pamdepth="${PBM_BINPREFIX}pamdepth"
+  alias pamseq="${PBM_BINPREFIX}pamseq"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  shopt -s expand_aliases
+
+pamseq 3 5 -tupletype=RGB | pamtopnm \
  > ${tmpdir}/palette
-${PBM_BINPREFIX}pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
+pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
 
 # Test 2. Default (unmodified quantization)
-${PBM_TESTPREFIX}pnmremap -mapfile=${tmpdir}/palette -nofloyd \
+pnmremap -mapfile=${tmpdir}/palette -nofloyd \
 testimg.ppm | cksum
 
 # Test 3. Use first color in palette for missing colors
-${PBM_TESTPREFIX}pnmremap -mapfile=${tmpdir}/palette255 -nofloyd \
+pnmremap -mapfile=${tmpdir}/palette255 -nofloyd \
  -firstisdefault testimg.ppm | cksum
 
 # Test 4. Use black for missing colors
-${PBM_TESTPREFIX}pnmremap -mapfile=${tmpdir}/palette255 -nofloyd \
+pnmremap -mapfile=${tmpdir}/palette255 -nofloyd \
 -missingcolor=black testimg.ppm | cksum
 
 rm ${tmpdir}/palette{,255}
diff --git a/test/pnmshear.ok b/test/pnmshear.ok
index d701faaf..058ec042 100644
--- a/test/pnmshear.ok
+++ b/test/pnmshear.ok
@@ -1 +1 @@
-2080980136 22
+598644601 24
diff --git a/test/pnmshear.test b/test/pnmshear.test
index 276e3e22..30ab45be 100644..100755
--- a/test/pnmshear.test
+++ b/test/pnmshear.test
@@ -1,19 +1,26 @@
 #! /bin/bash
 # This script tests: pnmshear
-# Also requires: pbmmake
+# Also requires: pbmmake pnmpad
 
-# Test.  Should produce 2080980136 22
-${PBM_BINPREFIX}pbmmake -g 7 7 | \
-   ${PBM_TESTPREFIX}pnmshear 45 -noantialias  | cksum
+  alias pnmshear="${PBM_TESTPREFIX}pnmshear"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pnmpad="${PBM_BINPREFIX}pnmpad"
+  shopt -s expand_aliases
+
+# Test.  Should produce 598644601 24
+
+pbmmake -g 7 7 | pnmpad -white -top 1 | \
+   pnmshear 45 -noantialias | cksum
 
 # Output of above, in pbm plain format
 #
 # P1
-# 14 7
-# 01010101111111
-# 10101011111111
-# 10101010111111
-# 11101010111111
-# 11101010101111
-# 11111010101111
-# 11111010101011
+# 15 8
+# 000000000000000
+# 010101000000000
+# 010101010000000
+# 000101010000000
+# 000101010100000
+# 000001010100000
+# 000001010101000
+# 000000010101000
diff --git a/test/pnmtile.test b/test/pnmtile.test
index 0f9d91df..f74bdce3 100755
--- a/test/pnmtile.test
+++ b/test/pnmtile.test
@@ -2,11 +2,15 @@
 # This script tests: pnmtile
 # Also requires: pnmcat
 
-${PBM_TESTPREFIX}pnmtile 40 50 testgrid.pbm | cksum
+  alias pnmtile="${PBM_TESTPREFIX}pnmtile"
+  alias pnmcat="${PBM_BINPREFIX}pnmcat"
+  shopt -s expand_aliases
 
-${PBM_TESTPREFIX}pnmtile 454 298 testimg.ppm > ${tmpdir}/testimg4.ppm &&
-${PBM_BINPREFIX}pnmcat -lr testimg.ppm testimg.ppm > ${tmpdir}/testimg2.ppm &&
-${PBM_BINPREFIX}pnmcat -tb ${tmpdir}/testimg2.ppm ${tmpdir}/testimg2.ppm | \
+pnmtile 40 50 testgrid.pbm | cksum
+
+pnmtile 454 298 testimg.ppm > ${tmpdir}/testimg4.ppm &&
+pnmcat -lr testimg.ppm testimg.ppm > ${tmpdir}/testimg2.ppm &&
+pnmcat -tb ${tmpdir}/testimg2.ppm ${tmpdir}/testimg2.ppm | \
 cmp -s - ${tmpdir}/testimg4.ppm
 echo $?
 
diff --git a/test/pnmtopnm-plain.test b/test/pnmtopnm-plain.test
index ff211e40..f4e0749e 100755
--- a/test/pnmtopnm-plain.test
+++ b/test/pnmtopnm-plain.test
@@ -2,9 +2,14 @@
 # This script tests: pnmtopnm
 # Also requires: pgmtopgm ppmtoppm pamtopnm
 
-${PBM_TESTPREFIX}pnmtopnm -plain testgrid.pbm
+  alias pnmtopnm="${PBM_TESTPREFIX}pnmtopnm"
+  alias pgmtopgm="${PBM_BINPREFIX}pgmtopgm"
+  alias ppmtoppm="${PBM_BINPREFIX}ppmtoppm"
+  shopt -s expand_aliases
 
-${PBM_BINPREFIX}pgmtopgm < testgrid.pbm | ${PBM_TESTPREFIX}pnmtopnm -plain
+pnmtopnm -plain testgrid.pbm
 
-${PBM_BINPREFIX}ppmtoppm < testgrid.pbm | ${PBM_TESTPREFIX}pnmtopnm -plain | \
+pgmtopgm < testgrid.pbm | pnmtopnm -plain
+
+ppmtoppm < testgrid.pbm | pnmtopnm -plain | \
   head -n11
diff --git a/test/ppmbrighten.test b/test/ppmbrighten.test
index 72bebff4..29cfeb2c 100755
--- a/test/ppmbrighten.test
+++ b/test/ppmbrighten.test
@@ -2,6 +2,9 @@
 # This script tests: ppmbrighten
 # Also requires: 
 
-${PBM_TESTPREFIX}ppmbrighten -v 100 testimg.ppm | cksum
-${PBM_TESTPREFIX}ppmbrighten -v 100 -normalize testimg.ppm | cksum
-${PBM_TESTPREFIX}ppmbrighten -s 100 -v -50 testimg.ppm | cksum
+  alias ppmbrighten="${PBM_TESTPREFIX}ppmbrighten"
+  shopt -s expand_aliases
+
+ppmbrighten -v 100 testimg.ppm | cksum
+ppmbrighten -v 100 -normalize testimg.ppm | cksum
+ppmbrighten -s 100 -v -50 testimg.ppm | cksum
diff --git a/test/ppmchange-roundtrip.test b/test/ppmchange-roundtrip.test
index c303c11b..a357870e 100755
--- a/test/ppmchange-roundtrip.test
+++ b/test/ppmchange-roundtrip.test
@@ -2,10 +2,16 @@
 # This script tests: ppmchange
 # Also requires: pgmtopbm pnminvert ppmtopgm
 
-${PBM_TESTPREFIX}ppmchange black white white black testgrid.pbm | \
-${PBM_BINPREFIX}pnminvert | ${PBM_BINPREFIX}ppmtopgm | \
-${PBM_BINPREFIX}pgmtopbm -th -val=0.5 | cksum
+  alias ppmchange="${PBM_TESTPREFIX}ppmchange"
+  alias pgmtopbm="${PBM_BINPREFIX}pgmtopbm"
+  alias pnminvert="${PBM_BINPREFIX}pnminvert"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
 
-${PBM_TESTPREFIX}ppmchange  black white white black testgrid.pbm | \
-${PBM_TESTPREFIX}ppmchange  black white white black -plain  | \
-${PBM_BINPREFIX}ppmtopgm | ${PBM_BINPREFIX}pgmtopbm -th -val=0.5 | cksum
+ppmchange black white white black testgrid.pbm | \
+pnminvert | ppmtopgm | \
+pgmtopbm -th -val=0.5 | cksum
+
+ppmchange  black white white black testgrid.pbm | \
+ppmchange  black white white black -plain  | \
+ppmtopgm | pgmtopbm -th -val=0.5 | cksum
diff --git a/test/ppmcie.test b/test/ppmcie.test
index 1e60c848..2584ece4 100755
--- a/test/ppmcie.test
+++ b/test/ppmcie.test
@@ -2,6 +2,11 @@
 # This script tests: ppmcie
 # Also requires: pamsumm pamsharpness
 
+  alias ppmcie="${PBM_TESTPREFIX}ppmcie"
+  alias pamsharpness="${PBM_BINPREFIX}pamsharpness"
+  alias pamsumm="${PBM_BINPREFIX}pamsumm"
+  shopt -s expand_aliases
+
 # Test 1. Should print 955840041 786447
 # Without -nolabel -noaxes -nowpoint -noblack older versions of
 # Netpbm produce slightly different charts.
@@ -9,7 +14,7 @@
 # v. 10.35.86: 288356530 786447   
 # v. 10.59.2 : 2292601420 786447  
 
-${PBM_TESTPREFIX}ppmcie -nolabel -noaxes -nowpoint -noblack \
+ppmcie -nolabel -noaxes -nowpoint -noblack \
  > ${tmpdir}/ppmcie.ppm
 
 # There is a slight difference in the output depending on whether ppmcie
@@ -26,7 +31,7 @@ ${PBM_TESTPREFIX}ppmcie -nolabel -noaxes -nowpoint -noblack \
 # x86 32 bit: 38.660173
 # x86 64 bit: 38.681432
 
-${PBM_BINPREFIX}pamsumm --mean --brief ${tmpdir}/ppmcie.ppm | \
+pamsumm --mean --brief ${tmpdir}/ppmcie.ppm | \
   awk '{ if(38.65 < $1 && $1 <38.69) print "ok"; else print $1}'
 
 # Test 2.  Measure image sharpness
@@ -34,7 +39,7 @@ ${PBM_BINPREFIX}pamsumm --mean --brief ${tmpdir}/ppmcie.ppm | \
 # x86 32 bit: 0.002476
 # x86 64 bit: 0.002478
 
-${PBM_BINPREFIX}pamsharpness ${tmpdir}/ppmcie.ppm 2>&1 | \
+pamsharpness ${tmpdir}/ppmcie.ppm 2>&1 | \
   awk 'NF==4 && $2=="Sharpness" \
        {if (0.002475 < $4 && $4 < 0.002479) print "ok"; else print $4}
        NF>0 && NF!=4 {print "error"}'
diff --git a/test/ppmdfont.test b/test/ppmdfont.test
index 027b8f2c..30f74a9c 100755
--- a/test/ppmdfont.test
+++ b/test/ppmdfont.test
@@ -2,11 +2,16 @@
 # This script tests: ppmdmkfont ppmddumpfont ppmdcfont
 # Also requires: 
 
+  alias ppmdcfont="${PBM_TESTPREFIX}ppmdcfont"
+  alias ppmddumpfont="${PBM_TESTPREFIX}ppmddumpfont"
+  alias ppmdmkfont="${PBM_TESTPREFIX}ppmdmkfont"
+  shopt -s expand_aliases
+
 # Test 1. Should produce: 2726488777 48129
-${PBM_TESTPREFIX}ppmdmkfont | ${PBM_TESTPREFIX}ppmddumpfont 2>&1 | cksum
+ppmdmkfont | ppmddumpfont 2>&1 | cksum
 
 # Test 2. Should produce: 2845495212 75033
-${PBM_TESTPREFIX}ppmdmkfont | ${PBM_TESTPREFIX}ppmdcfont | cksum
+ppmdmkfont | ppmdcfont | cksum
 
 # There is a strange glitch in output when ppmdcfont is compiled by clang:
 # 3171,3173c3171,3173
diff --git a/test/ppmdim.test b/test/ppmdim.test
index e2a1b0df..e528c3ce 100755
--- a/test/ppmdim.test
+++ b/test/ppmdim.test
@@ -2,6 +2,12 @@
 # This script tests: ppmdim
 # Also requires: pamfunc pnmarith pamarith pamsumm
 
+  alias ppmdim="${PBM_TESTPREFIX}ppmdim"
+  alias pamfunc="${PBM_BINPREFIX}pamfunc"
+  alias pamsumm="${PBM_BINPREFIX}pamsumm"
+  alias pnmarith="${PBM_BINPREFIX}pnmarith"
+  shopt -s expand_aliases
+
 # Compare ppmdim and pamfunc with various dim factors
 # Due to the difference in rounding methods, pamfunc produces slightly
 # brighter images, by about 0.5 per pixel.
@@ -10,10 +16,10 @@
 
 for i in  0.125 0.25 0.5 0.75 0.1 0.0117 0.2 0.4 0.333 0.666 0.8 0.9 0.95
   do
-  ${PBM_TESTPREFIX}ppmdim $i testimg.ppm > ${tmpdir}/dim1.ppm
-  ${PBM_BINPREFIX}pamfunc -mult=$i testimg.ppm > ${tmpdir}/dim2.ppm
-  ${PBM_BINPREFIX}pnmarith -diff ${tmpdir}/dim1.ppm ${tmpdir}/dim2.ppm | \
-    ${PBM_BINPREFIX}pamsumm -mean -brief | \
+  ppmdim $i testimg.ppm > ${tmpdir}/dim1.ppm
+  pamfunc -mult=$i testimg.ppm > ${tmpdir}/dim2.ppm
+  pnmarith -diff ${tmpdir}/dim1.ppm ${tmpdir}/dim2.ppm | \
+    pamsumm -mean -brief | \
     awk '{print $1<0.75 ? "ok" : "fail"}'
   done
 rm ${tmpdir}/dim[12].ppm
diff --git a/test/ppmdither.test b/test/ppmdither.test
index 5fea0bb4..a93a434a 100755
--- a/test/ppmdither.test
+++ b/test/ppmdither.test
@@ -2,6 +2,9 @@
 # This script tests: ppmdither
 # Also requires: 
 
-${PBM_TESTPREFIX}ppmdither testimg.ppm | cksum
-${PBM_TESTPREFIX}ppmdither -red 2 -green 2 -blue 2 testimg.ppm | cksum
-${PBM_TESTPREFIX}ppmdither -dim 2 testimg.ppm | cksum
+  alias ppmdither="${PBM_TESTPREFIX}ppmdither"
+  shopt -s expand_aliases
+
+ppmdither testimg.ppm | cksum
+ppmdither -red 2 -green 2 -blue 2 testimg.ppm | cksum
+ppmdither -dim 2 testimg.ppm | cksum
diff --git a/test/ppmforge.test b/test/ppmforge.test
index 5ec37e9c..09a66916 100755
--- a/test/ppmforge.test
+++ b/test/ppmforge.test
@@ -1,6 +1,9 @@
 #! /bin/bash
 # This script tests: ppmforge
 
+  alias ppmforge="${PBM_TESTPREFIX}ppmforge"
+  shopt -s expand_aliases
+
 # Use small x y values to avoid floating point issues.
 
 
@@ -8,7 +11,7 @@ testrandom -q
 case $? in
    81)
       # Test 1: Should print: 3634219838 196623
-      ${PBM_TESTPREFIX}ppmforge -night -seed 1 | cksum
+      ppmforge -night -seed 1 | cksum
       ;;
 
    8[02-9] | 90)
diff --git a/test/ppmgauss.test b/test/ppmgauss.test
index a75d272c..9dfdd2f6 100755
--- a/test/ppmgauss.test
+++ b/test/ppmgauss.test
@@ -2,10 +2,13 @@
 # This script tests: pamgauss
 # Also requires: 
 
+  alias pamgauss="${PBM_TESTPREFIX}pamgauss"
+  shopt -s expand_aliases
+
 for i in `seq 3 11`
 do
 for s in `seq 1 9`
 do
-${PBM_TESTPREFIX}pamgauss $i $i -sigma=.$s | cksum
+pamgauss $i $i -sigma=.$s | cksum
 done
 done
diff --git a/test/ppmhist.test b/test/ppmhist.test
index 95fcd2f7..6dcbc762 100755
--- a/test/ppmhist.test
+++ b/test/ppmhist.test
@@ -2,6 +2,10 @@
 # This script tests: ppmhist
 # Also requires: pgmramp
 
-${PBM_BINPREFIX}pgmramp -maxval=8 -lr 8 2 | ${PBM_TESTPREFIX}ppmhist -sort=rgb
-${PBM_TESTPREFIX}ppmhist -map -sort=rgb testimg.ppm | cksum
+  alias ppmhist="${PBM_TESTPREFIX}ppmhist"
+  alias pgmramp="${PBM_BINPREFIX}pgmramp"
+  shopt -s expand_aliases
+
+pgmramp -maxval=8 -lr 8 2 | ppmhist -sort=rgb
+ppmhist -map -sort=rgb testimg.ppm | cksum
 
diff --git a/test/ppmmake.test b/test/ppmmake.test
index da23095c..faba1ace 100755
--- a/test/ppmmake.test
+++ b/test/ppmmake.test
@@ -2,10 +2,13 @@
 # This script tests: ppmmake
 # Also requires: 
 
-${PBM_TESTPREFIX}ppmmake rgb:ff/80/80 50 100 -maxval=5 | cksum
-${PBM_TESTPREFIX}ppmmake red 50 50  | cksum
+  alias ppmmake="${PBM_TESTPREFIX}ppmmake"
+  shopt -s expand_aliases
 
+ppmmake rgb:ff/80/80 50 100 -maxval=5 | cksum
+ppmmake red 50 50  | cksum
 
 
 
- 
\ No newline at end of file
+
+ 
diff --git a/test/ppmmix.test b/test/ppmmix.test
index 451aa8be..d24e589d 100755
--- a/test/ppmmix.test
+++ b/test/ppmmix.test
@@ -3,26 +3,36 @@
 # Also requires: pamdepth pamenlarge pamsumm pbmmake
 # Also requires: pgmtopgm pnminvert ppmtopgm
 
+  alias ppmmix="${PBM_TESTPREFIX}ppmmix"
+  alias pamdepth="${PBM_BINPREFIX}pamdepth"
+  alias pamenlarge="${PBM_BINPREFIX}pamenlarge"
+  alias pamsumm="${PBM_BINPREFIX}pamsumm"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pgmtopgm="${PBM_BINPREFIX}pgmtopgm"
+  alias pnminvert="${PBM_BINPREFIX}pnminvert"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Print a pretty checkerboard pattern
-${PBM_BINPREFIX}pbmmake -g 8 8 | \
- ${PBM_BINPREFIX}pgmtopgm > ${tmpdir}/a1.pgm &&
-${PBM_BINPREFIX}pbmmake -g 2 2 | ${PBM_BINPREFIX}pamenlarge 4 | \
- ${PBM_BINPREFIX}pgmtopgm > ${tmpdir}/a2.pgm &&
-${PBM_TESTPREFIX}ppmmix 0.75 ${tmpdir}/a1.pgm ${tmpdir}/a2.pgm -plain | \
- ${PBM_BINPREFIX}ppmtopgm | ${PBM_BINPREFIX}pamdepth 3 -plain &&
+pbmmake -g 8 8 | \
+ pgmtopgm > ${tmpdir}/a1.pgm &&
+pbmmake -g 2 2 | pamenlarge 4 | \
+ pgmtopgm > ${tmpdir}/a2.pgm &&
+ppmmix 0.75 ${tmpdir}/a1.pgm ${tmpdir}/a2.pgm -plain | \
+ ppmtopgm | pamdepth 3 -plain &&
 rm ${tmpdir}/a1.pgm ${tmpdir}/a2.pgm
 
 # Mix image with itself.
 # Output should match input regardless of ratio. 
 for i in 0 0.5 0.6 1
 do 
-${PBM_TESTPREFIX}ppmmix $i testimg.ppm testimg.ppm | cksum
+ppmmix $i testimg.ppm testimg.ppm | cksum
 done
 
 # Mix image with its own inverse.
 # Output should be a monotone gray sheet.
-${PBM_BINPREFIX}pnminvert testimg.ppm | ${PBM_TESTPREFIX}ppmmix .5 \
+pnminvert testimg.ppm | ppmmix .5 \
     testimg.ppm - | tee ${tmpdir}/a3.ppm | \
-  ${PBM_BINPREFIX}pamsumm -brief -max &&
-  ${PBM_BINPREFIX}pamsumm -brief -min ${tmpdir}/a3.ppm &&
+  pamsumm -brief -max &&
+  pamsumm -brief -min ${tmpdir}/a3.ppm &&
 rm ${tmpdir}/a3.ppm
diff --git a/test/ppmpat.test b/test/ppmpat.test
index af0ce956..bf1e9316 100755
--- a/test/ppmpat.test
+++ b/test/ppmpat.test
@@ -1,6 +1,9 @@
 #! /bin/bash
 # This script tests: ppmpat
 
+  alias ppmpat="${PBM_TESTPREFIX}ppmpat"
+  shopt -s expand_aliases
+
 # TODO: Write tests for squig and poles.  It appears that they are
 # sensitive to differences in floating point math.
 
@@ -8,22 +11,22 @@ testrandom -q
 case $? in
    81)
        # Test 1. Should print: 4008533639 781
-       ${PBM_TESTPREFIX}ppmpat --randomseed=0 -g2 16 16 | cksum
+       ppmpat --randomseed=0 -g2 16 16 | cksum
 
        # Test 2. Should print: 2448908863 9613
-       ${PBM_TESTPREFIX}ppmpat --randomseed=0 -g2 64 50 | cksum
+       ppmpat --randomseed=0 -g2 64 50 | cksum
 
        # Test 3. Should print: 2698433077 1549
-       ${PBM_TESTPREFIX}ppmpat --randomseed=0 -madras 32 16 | cksum
+       ppmpat --randomseed=0 -madras 32 16 | cksum
 
        # Test 4. Should print: 3705929501 781
-       ${PBM_TESTPREFIX}ppmpat --randomseed=0 -tartan 16 16 | cksum
+       ppmpat --randomseed=0 -tartan 16 16 | cksum
 
        # Test 5. Should print: 2219119109 36015
-       ${PBM_TESTPREFIX}ppmpat --randomseed=0 -camo 100 120 | cksum
+       ppmpat --randomseed=0 -camo 100 120 | cksum
 
        # Test 6. Should print: 3436846137 16813
-       ${PBM_TESTPREFIX}ppmpat --randomseed=0 -anticamo 80 70 | cksum
+       ppmpat --randomseed=0 -anticamo 80 70 | cksum
        ;;
 
    8[02-9] | 90)
diff --git a/test/ppmrelief.ok b/test/ppmrelief.ok
new file mode 100644
index 00000000..3c236775
--- /dev/null
+++ b/test/ppmrelief.ok
@@ -0,0 +1,3 @@
+3637356697 688
+3979143778 780
+2954951371 780
diff --git a/test/ppmrelief.test b/test/ppmrelief.test
new file mode 100755
index 00000000..378a22c9
--- /dev/null
+++ b/test/ppmrelief.test
@@ -0,0 +1,42 @@
+#! /bin/bash
+# This script tests: ppmrelief
+# Also requires: pbmmake pgmramp pamflip
+
+  alias ppmrelief="${PBM_TESTPREFIX}ppmrelief"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pgmramp="${PBM_BINPREFIX}pgmramp"
+  alias pamflip="${PBM_BINPREFIX}pamflip"
+  shopt -s expand_aliases
+
+# Test 1.  Should print 3637356697 688
+pbmmake -w 15 15 | ppmrelief | cksum
+
+# Test 2.  Should print 3979143778 780
+pgmramp -diagonal -maxval=30 16 16 | ppmrelief | cksum
+
+# Test 3.  Should print 2954951371 780
+pgmramp -diagonal -maxval=30 16 16 | pamflip -lr | ppmrelief | cksum
+
+
+# pgmramp -diagonal -maxval=30 16 16 | ppmrelief | ppmtopgm -plain
+
+# P2
+# 16 16
+# 30
+# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+
diff --git a/test/ppmrough.test b/test/ppmrough.test
index 92cc02d7..dbe92c29 100755
--- a/test/ppmrough.test
+++ b/test/ppmrough.test
@@ -1,12 +1,15 @@
 #! /bin/bash
 # This script tests: ppmrough
 
+  alias ppmrough="${PBM_TESTPREFIX}ppmrough"
+  shopt -s expand_aliases
+
 
 testrandom -q
 case $? in
    81)
       # Should print: 378403602 30015
-      ${PBM_TESTPREFIX}ppmrough  -randomseed 1 | cksum
+      ppmrough  -randomseed 1 | cksum
       ;;
 
    8[02-9] | 90)
diff --git a/test/ppmtopgm.test b/test/ppmtopgm.test
index 7ab481c6..94e0ae91 100755
--- a/test/ppmtopgm.test
+++ b/test/ppmtopgm.test
@@ -2,6 +2,9 @@
 # This script tests: ppmtopgm
 # Also requires: 
 
+  alias ppmtopgm="${PBM_TESTPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Test 1. Should produce 2871603838 33838
-${PBM_TESTPREFIX}ppmtopgm testimg.ppm | cksum
+ppmtopgm testimg.ppm | cksum
 
diff --git a/test/ppmtoppm.test b/test/ppmtoppm.test
index c073ecff..2d84809a 100755
--- a/test/ppmtoppm.test
+++ b/test/ppmtoppm.test
@@ -2,4 +2,7 @@
 # This script tests: ppmtoppm
 # Also requires: 
 
-${PBM_TESTPREFIX}ppmtoppm < testgrid.pbm | cksum
\ No newline at end of file
+  alias ppmtoppm="${PBM_TESTPREFIX}ppmtoppm"
+  shopt -s expand_aliases
+
+ppmtoppm < testgrid.pbm | cksum
diff --git a/test/ppmwheel.test b/test/ppmwheel.test
index 4d786f26..89bab029 100755
--- a/test/ppmwheel.test
+++ b/test/ppmwheel.test
@@ -2,12 +2,15 @@
 # This script tests: ppmwheel
 # Also requires: 
 
+  alias ppmwheel="${PBM_TESTPREFIX}ppmwheel"
+  shopt -s expand_aliases
+
 # For values 6 and above, x86(-32) and x86-64 produce different output.
 # SSE floating-point math is the probable cause.
 
 for i in 4 5
 do
-${PBM_TESTPREFIX}ppmwheel $i | cksum
+ppmwheel $i | cksum
 done
 
 #   i    32 bit              64 bit
@@ -110,4 +113,4 @@ done
 
 
 
- 
\ No newline at end of file
+ 
diff --git a/test/ps-alt-roundtrip.test b/test/ps-alt-roundtrip.test
index 8ce1689d..8c4cfc2a 100755
--- a/test/ps-alt-roundtrip.test
+++ b/test/ps-alt-roundtrip.test
@@ -2,6 +2,14 @@
 # This script tests: pbmtoepsi pbmtopsg3 pbmtolps psidtopgm pstopnm
 # Also requires: gs pnmtopnm pnmcrop
 
+  alias pbmtoepsi="${PBM_TESTPREFIX}pbmtoepsi"
+  alias pbmtolps="${PBM_TESTPREFIX}pbmtolps"
+  alias pbmtopsg3="${PBM_TESTPREFIX}pbmtopsg3"
+  alias psidtopgm="${PBM_TESTPREFIX}psidtopgm"
+  alias pstopnm="${PBM_TESTPREFIX}pstopnm"
+  alias pnmcrop="${PBM_BINPREFIX}pnmcrop"
+  shopt -s expand_aliases
+
 # This script is for testing alternative (or minor) utilities that
 # read/write Postscript and encapsulated Postscript:
 # pbmtoepsi, pbmtopsg3, pbmtolps and psidtopgm.
@@ -14,33 +22,35 @@
 # If ps-roundtrip.test succeeds and this test fails, it is most likely
 # a problem with one of the minor utilities, and vice versa.
 
+# pstopnm does not use libnetpbm functions for output. 
+# Output is filtered through pnmtopnm.
 
 # Test 1. Should print: 2425386270 41
-${PBM_TESTPREFIX}pbmtopsg3 -dpi=72 testgrid.pbm \
+pbmtopsg3 -dpi=72 testgrid.pbm \
      > ${tmpdir}/testgrid1.ps && \
-${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 -llx=0 -lly=-16 -urx=14 \
+pstopnm -xborder=0 -yborder=0 -llx=0 -lly=-16 -urx=14 \
     -dpi=72 -stdout -quiet -pbm ${tmpdir}/testgrid1.ps | \
-    ${PBM_BINPREFIX}pnmcrop | cksum
+    pnmcrop | cksum
 
 
 # Test 2. Should print: 2425386270 41
-${PBM_TESTPREFIX}pbmtolps -dpi 72 testgrid.pbm \
+pbmtolps -dpi 72 testgrid.pbm \
      > ${tmpdir}/testgrid2.ps && \
-${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 -dpi=72 -stdout \
+pstopnm -xborder=0 -yborder=0 -dpi=72 -stdout \
     -quiet ${tmpdir}/testgrid2.ps -pbm | \
-  ${PBM_BINPREFIX}pnmcrop | cksum
+  pnmcrop | cksum
 
 
 # Test 3. Should print: 2916080186 235
 # Output is pgm maxval=1 with black and white inverted.
 #
-${PBM_TESTPREFIX}pbmtoepsi testgrid.pbm > ${tmpdir}/testgrid.epsi && \
+pbmtoepsi testgrid.pbm > ${tmpdir}/testgrid.epsi && \
 xysizebps=`awk  '/BeginPreview/ {print $2,$3,$4}' \
     ${tmpdir}/testgrid.epsi` && \
 awk '/^%%BeginPreview:/ { p=1; next } /^%%EndImage/ { p=0; next } \
   p==1 && /%[ \t0-9a-fA-F]+/ { print substr($0,2); next } \
   p==1 {print "!"$0}' \
-    ${tmpdir}/testgrid.epsi | ${PBM_TESTPREFIX}psidtopgm $xysizebps | cksum
+    ${tmpdir}/testgrid.epsi | psidtopgm $xysizebps | cksum
 
 
 rm ${tmpdir}/testgrid[12].ps  ${tmpdir}/testgrid.epsi 
diff --git a/test/ps-roundtrip.test b/test/ps-roundtrip.test
index 92925064..1877724d 100755
--- a/test/ps-roundtrip.test
+++ b/test/ps-roundtrip.test
@@ -2,41 +2,58 @@
 # This script tests: pnmtops pstopnm
 # Also requires: pnmtopnm pamtopnm gs pbmmake pnmshear pnmpad pnmcat
 
+  alias pnmtops="${PBM_TESTPREFIX}pnmtops"
+  alias pstopnm="${PBM_TESTPREFIX}pstopnm"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pnmcat="${PBM_BINPREFIX}pnmcat"
+  alias pnmpad="${PBM_BINPREFIX}pnmpad"
+  alias pnmshear="${PBM_BINPREFIX}pnmshear"
+  alias pnmtopnm="${PBM_BINPREFIX}pnmtopnm"
+  shopt -s expand_aliases
+
+# Failure message
+## This test fails when:
+## (1) zlib was not linked.
+## (2) ghostscript is not available.
+
+# pstopnm does not use libnetpbm functions for output. 
+# Output is filtered through pnmtopnm.
+
 # Test 1.  Should print: 1926073387 101484 five times
 # *NOTE* Fifth iteration fails if pnmtops was compiled without zlib
 # (flate compression) support.
 for flag in "" "-ps" "-rle" "-ps -ascii" "-ps -flate"
   do
-  ${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn \
+  pnmtops -nocenter -equalpixels -dpi 72 -noturn \
     ${flag} testimg.ppm \
     > ${tmpdir}/testimg.ps
   xysize1=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \
     ${tmpdir}/testimg.ps` 
-  ${PBM_TESTPREFIX}pstopnm -portrait -xborder=0 -yborder=0 $xysize1 -stdout \
+  pstopnm -portrait -xborder=0 -yborder=0 $xysize1 -stdout \
     -quiet ${tmpdir}/testimg.ps | \
-    ${PBM_BINPREFIX}pnmtopnm | cksum
+    pnmtopnm | cksum
   done
 
 
 # Test 2.  Should print: 2918318199 62 seven times
 # Test image designed to detect problems with run-length compression
 #
-${PBM_BINPREFIX}pbmmake -g 2 2 > ${tmpdir}/g.pbm
-${PBM_BINPREFIX}pbmmake -g 8 4 | \
-  ${PBM_BINPREFIX}pnmshear 45 -noantialias -background=black | \
-  ${PBM_BINPREFIX}pnmpad -right 60 | \
-  ${PBM_BINPREFIX}pnmcat -tb -jright - ${tmpdir}/g.pbm > ${tmpdir}/t.pbm &&
+pbmmake -g 2 2 > ${tmpdir}/g.pbm
+pbmmake -g 8 4 | \
+  pnmshear 45 -noantialias -background=black | \
+  pnmpad -right 60 | \
+  pnmcat -tb -jright - ${tmpdir}/g.pbm > ${tmpdir}/t.pbm &&
 for flag in "" "-rle" "-ps -rle -ascii" \
             "-bitspersample=2 -rle" "-ps -bitspersample=4 -rle" \
             "-bitspersample=8 -rle" "-ps -bitspersample=12 -rle -dict" 
   do
-  ${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn \
+  pnmtops -nocenter -equalpixels -dpi 72 -noturn \
     ${flag} ${tmpdir}/t.pbm  > ${tmpdir}/testgrid.ps &&
   xysize2=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \
     ${tmpdir}/testgrid.ps`
-  ${PBM_TESTPREFIX}pstopnm -portrait -xborder=0 -yborder=0 $xysize2 -stdout \
+  pstopnm -portrait -xborder=0 -yborder=0 $xysize2 -stdout \
     -quiet ${tmpdir}/testgrid.ps -pbm | \
-    ${PBM_BINPREFIX}pnmtopnm | cksum
+    pnmtopnm | cksum
   done
 
 
@@ -56,13 +73,13 @@ for flag in "" "-ps" \
             "-ps -bitspersample=12 -flate -rle -vmreclaim"
   do
 cat testimg.ppm testimg.ppm testimg.ppm testgrid.pbm testgrid.pbm | \
-${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage \
+pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage \
   ${flag}  > ${tmpdir}/testimg5.ps
 xysize3=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5 ; exit}' \
   ${tmpdir}/testimg5.ps`
-${PBM_TESTPREFIX}pstopnm -portrait -xborder=0 -yborder=0 $xysize3 \
+pstopnm -portrait -xborder=0 -yborder=0 $xysize3 \
     -stdout  ${tmpdir}/testimg5.ps | \
-  ${PBM_BINPREFIX}pnmtopnm | cksum
+  pnmtopnm | cksum
   done
 
 
diff --git a/test/rgb3-roundtrip.test b/test/rgb3-roundtrip.test
index d3575b95..829feab4 100755
--- a/test/rgb3-roundtrip.test
+++ b/test/rgb3-roundtrip.test
@@ -2,13 +2,20 @@
 # This script tests: ppmtorgb3 rgb3toppm
 # Also requires: pgmtopbm pgmtopgm ppmtopgm
 
+  alias ppmtorgb3="${PBM_TESTPREFIX}ppmtorgb3"
+  alias rgb3toppm="${PBM_TESTPREFIX}rgb3toppm"
+  alias pgmtopbm="${PBM_BINPREFIX}pgmtopbm"
+  alias pgmtopgm="${PBM_BINPREFIX}pgmtopgm"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Break an image into three monochrome planes, reassemble the
 # image from them and check whether the resulting output is
 # identical to the original input.
 
 cp testimg.ppm ${tmpdir} &&
-${PBM_TESTPREFIX}ppmtorgb3 ${tmpdir}/testimg.ppm &&
-${PBM_TESTPREFIX}rgb3toppm ${tmpdir}/testimg.red ${tmpdir}/testimg.grn \
+ppmtorgb3 ${tmpdir}/testimg.ppm &&
+rgb3toppm ${tmpdir}/testimg.red ${tmpdir}/testimg.grn \
   ${tmpdir}/testimg.blu | cksum
 
 cat ${tmpdir}/testimg.red ${tmpdir}/testimg.grn ${tmpdir}/testimg.blu | \
@@ -17,10 +24,10 @@ cat ${tmpdir}/testimg.red ${tmpdir}/testimg.grn ${tmpdir}/testimg.blu | \
 rm ${tmpdir}/testimg.{ppm,red,grn,blu}
 
 cp testgrid.pbm ${tmpdir} &&
-${PBM_TESTPREFIX}ppmtorgb3 ${tmpdir}/testgrid.pbm &&
-${PBM_TESTPREFIX}rgb3toppm ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn \
+ppmtorgb3 ${tmpdir}/testgrid.pbm &&
+rgb3toppm ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn \
   ${tmpdir}/testgrid.blu | \
-  ${PBM_BINPREFIX}ppmtopgm | ${PBM_BINPREFIX}pgmtopbm -th -val=0.5 | cksum
+  ppmtopgm | pgmtopbm -th -val=0.5 | cksum
 
 
 # With PGM or PBM input, the three monochrome planes should be
@@ -28,7 +35,7 @@ ${PBM_TESTPREFIX}rgb3toppm ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn \
 
 cmp -s ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn ; echo $?
 cmp -s ${tmpdir}/testgrid.grn ${tmpdir}/testgrid.blu ; echo $?
-${PBM_BINPREFIX}pgmtopgm < testgrid.pbm | cmp -s - ${tmpdir}/testgrid.red
+pgmtopgm < testgrid.pbm | cmp -s - ${tmpdir}/testgrid.red
   echo $?
 
 rm ${tmpdir}/testgrid.{pbm,red,grn,blu}
diff --git a/test/sunrast-roundtrip.test b/test/sunrast-roundtrip.test
index ee5b25e3..78e4ae15 100755
--- a/test/sunrast-roundtrip.test
+++ b/test/sunrast-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pnmtorast rasttopnm
 # Also requires: 
 
+  alias pnmtorast="${PBM_TESTPREFIX}pnmtorast"
+  alias rasttopnm="${PBM_TESTPREFIX}rasttopnm"
+  shopt -s expand_aliases
+
 # Should produce 1926073387 101484, cksum of testimg.ppm
-${PBM_TESTPREFIX}pnmtorast testimg.ppm | ${PBM_TESTPREFIX}rasttopnm | cksum 
+pnmtorast testimg.ppm | rasttopnm | cksum 
diff --git a/test/symmetry.ok b/test/symmetry.ok
new file mode 100644
index 00000000..23129684
--- /dev/null
+++ b/test/symmetry.ok
@@ -0,0 +1,12 @@
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
diff --git a/test/symmetry.test b/test/symmetry.test
new file mode 100755
index 00000000..5fdb3918
--- /dev/null
+++ b/test/symmetry.test
@@ -0,0 +1,95 @@
+#! /bin/bash
+# This script tests: pgmramp pamgauss pgmkernel pbmpscale
+# Also requires: pamflip
+
+  alias pgmramp="${PBM_TESTPREFIX}pgmramp"
+  alias pamgauss="${PBM_TESTPREFIX}pamgauss"
+  alias pgmkernel="${PBM_TESTPREFIX}pgmkernel"
+  alias pbmpscale="${PBM_TESTPREFIX}pbmpscale"
+  alias pamflip="${PBM_BINPREFIX}pamflip"
+  shopt -s expand_aliases
+
+# All tests print "ok" upon success, cksum results otherwise.
+# The "$3>0" is a kludge for preventing false positives with empty files.
+
+# All test images are square and have the symmetries of the square (Dih4).
+# The sole exception is ell.pgm which is a rectangle (Dih2, also called
+# "Klein four-group".)
+
+# Example symmetric square PGM image:
+# P2
+# 5 5
+# 6
+# 1 2 3 2 1
+# 2 4 5 4 2
+# 3 5 6 5 3
+# 2 4 5 4 2
+# 1 2 3 2 1
+
+## Failure with this test indicates that a generator or editor which
+## should produce symmetric output images isn't doing so.
+
+# Test 1.
+pgmramp -rect 31 31 > ${tmpdir}/rect.pgm
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${tmpdir}/rect.pgm | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${tmpdir}/rect.pgm
+
+# Test 2.
+pgmramp -ell 63 63 > ${tmpdir}/circle.pgm
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${tmpdir}/circle.pgm | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${tmpdir}/circle.pgm
+
+# Test 3.
+pamgauss -sigma=0.1 -tupletype=GRAYSCALE 25 25 > ${tmpdir}/gauss.pam
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${tmpdir}/gauss.pam | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${tmpdir}/gauss.pam
+
+# Test 4.
+pgmkernel 15 15 > ${tmpdir}/kernel.pgm
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${tmpdir}/kernel.pgm | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${tmpdir}/kernel.pgm
+
+# Test 5.
+# Should print "ok" 7 times.
+for size in `seq 1 7`
+do
+pbmmake -g 5 5 | pbmpscale $size > ${tmpdir}/pscale.pbm
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${tmpdir}/pscale.pbm | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${tmpdir}/pscale.pbm
+done
+
+# Test 6.
+pgmramp -ell 101 33 > ${tmpdir}/ell.pgm
+
+( for op in -null -tb -lr
+  do pamflip $op ${tmpdir}/ell.pgm | cksum
+  done ) | uniq -c | \
+  awk '$1==3 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${tmpdir}/ell.pgm
+
diff --git a/test/targa-roundtrip.test b/test/targa-roundtrip.test
index 3fac1630..4a99e0e0 100755
--- a/test/targa-roundtrip.test
+++ b/test/targa-roundtrip.test
@@ -2,20 +2,26 @@
 # This script tests: pamtotga tgatoppm
 # Also requires: ppmtopgm pgmtopbm
 
+  alias pamtotga="${PBM_TESTPREFIX}pamtotga"
+  alias tgatoppm="${PBM_TESTPREFIX}tgatoppm"
+  alias pgmtopbm="${PBM_BINPREFIX}pgmtopbm"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 #Test 1: Should print 2425386270 41, cksum of testgrid.pbm
 
-${PBM_TESTPREFIX}pamtotga -mono testgrid.pbm | \
-  ${PBM_TESTPREFIX}tgatoppm | ${PBM_BINPREFIX}ppmtopgm | \
-  ${PBM_BINPREFIX}pgmtopbm -threshold -val 0.5 | cksum
+pamtotga -mono testgrid.pbm | \
+  tgatoppm | ppmtopgm | \
+  pgmtopbm -threshold -val 0.5 | cksum
 
 #Test 2: Should print 2871603838 33838, cksum of testimg.pgm
 
-${PBM_BINPREFIX}ppmtopgm testimg.ppm > ${tmpdir}/testimg.pgm
-${PBM_TESTPREFIX}pamtotga -cmap ${tmpdir}/testimg.pgm | \
-  ${PBM_TESTPREFIX}tgatoppm | ${PBM_BINPREFIX}ppmtopgm | cksum
+ppmtopgm testimg.ppm > ${tmpdir}/testimg.pgm
+pamtotga -cmap ${tmpdir}/testimg.pgm | \
+  tgatoppm | ppmtopgm | cksum
 
 rm ${tmpdir}/testimg.pgm
 
 #Test 3: Should print 1926073387 101484, cksum of testimg.ppm
 
-${PBM_TESTPREFIX}pamtotga -rgb testimg.ppm | ${PBM_TESTPREFIX}tgatoppm | cksum
+pamtotga -rgb testimg.ppm | tgatoppm | cksum
diff --git a/test/tiff-roundtrip.test b/test/tiff-roundtrip.test
index e7f875cf..410148a5 100755
--- a/test/tiff-roundtrip.test
+++ b/test/tiff-roundtrip.test
@@ -2,19 +2,25 @@
 # This script tests: pamtotiff tifftopnm
 # Also requires: 
 
-${PBM_TESTPREFIX}pamtotiff testimg.ppm 1<>${tmpdir}/test1.tiff &&
-  ${PBM_TESTPREFIX}tifftopnm ${tmpdir}/test1.tiff | cksum
+  alias pamtotiff="${PBM_TESTPREFIX}pamtotiff"
+  alias tifftopnm="${PBM_TESTPREFIX}tifftopnm"
+  shopt -s expand_aliases
+
+# Failure message
+## Second test fails if Netpbm was built without the flate library
+
+pamtotiff testimg.ppm 1<>${tmpdir}/test1.tiff &&
+  tifftopnm ${tmpdir}/test1.tiff | cksum
 
 # test flate compression 
-# Will fail if Netpbm was built without the flate library
-${PBM_TESTPREFIX}pamtotiff -flate testimg.ppm 1<>${tmpdir}/test2.tiff &&
-  ${PBM_TESTPREFIX}tifftopnm ${tmpdir}/test2.tiff | cksum
+pamtotiff -flate testimg.ppm 1<>${tmpdir}/test2.tiff &&
+  tifftopnm ${tmpdir}/test2.tiff | cksum
 
-${PBM_TESTPREFIX}pamtotiff testgrid.pbm 1<>${tmpdir}/test3.tiff &&
-  ${PBM_TESTPREFIX}tifftopnm ${tmpdir}/test3.tiff | cksum
+pamtotiff testgrid.pbm 1<>${tmpdir}/test3.tiff &&
+  tifftopnm ${tmpdir}/test3.tiff | cksum
 
 # test G4 compression 
-${PBM_TESTPREFIX}pamtotiff -g4 testgrid.pbm 1<>${tmpdir}/test4.tiff &&
-  ${PBM_TESTPREFIX}tifftopnm ${tmpdir}/test4.tiff | cksum
+pamtotiff -g4 testgrid.pbm 1<>${tmpdir}/test4.tiff &&
+  tifftopnm ${tmpdir}/test4.tiff | cksum
 
 rm ${tmpdir}/test[1234].tiff
diff --git a/test/utahrle-roundtrip.test b/test/utahrle-roundtrip.test
index 7113a2d5..83bc6c90 100755
--- a/test/utahrle-roundtrip.test
+++ b/test/utahrle-roundtrip.test
@@ -2,13 +2,18 @@
 # This script tests: pnmtorle rletopnm
 # Also requires: ppmtopgm
 
+  alias pnmtorle="${PBM_TESTPREFIX}pnmtorle"
+  alias rletopnm="${PBM_TESTPREFIX}rletopnm"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 #Test 1.  Should print 2871603838 33838, cksum of testimg.pgm
-${PBM_BINPREFIX}ppmtopgm testimg.ppm > ${tmpdir}/testimg.pgm
-${PBM_TESTPREFIX}pnmtorle ${tmpdir}/testimg.pgm | \
-  ${PBM_TESTPREFIX}rletopnm | cksum
+ppmtopgm testimg.ppm > ${tmpdir}/testimg.pgm
+pnmtorle ${tmpdir}/testimg.pgm | \
+  rletopnm | cksum
 
 rm ${tmpdir}/testimg.pgm
 
 #Test 2.  Should print 1926073387 101484, cksum of testimg.ppm
-${PBM_TESTPREFIX}pnmtorle testimg.ppm | \
-  ${PBM_TESTPREFIX}rletopnm | cksum
+pnmtorle testimg.ppm | \
+  rletopnm | cksum
diff --git a/test/wbmp-roundtrip.test b/test/wbmp-roundtrip.test
index bab2b6a5..40988635 100755
--- a/test/wbmp-roundtrip.test
+++ b/test/wbmp-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtowbmp wbmptopbm
 # Also requires: 
 
+  alias pbmtowbmp="${PBM_TESTPREFIX}pbmtowbmp"
+  alias wbmptopbm="${PBM_TESTPREFIX}wbmptopbm"
+  shopt -s expand_aliases
+
 # Should print 2425386270 41, cksum of testgrid.pbm
-${PBM_TESTPREFIX}pbmtowbmp testgrid.pbm | ${PBM_TESTPREFIX}wbmptopbm | cksum
+pbmtowbmp testgrid.pbm | wbmptopbm | cksum
diff --git a/test/winicon-roundtrip.test b/test/winicon-roundtrip.test
index f5f2926e..20b0a225 100755
--- a/test/winicon-roundtrip.test
+++ b/test/winicon-roundtrip.test
@@ -2,12 +2,21 @@
 # This script tests: pamtowinicon winicontopam
 # Also requires: pnmcut pnmtile pamtopnm ppmtopgm pgmtopbm
 
-${PBM_BINPREFIX}pnmcut --left=30 --width=48 --height=48 testimg.ppm | \
-${PBM_TESTPREFIX}pamtowinicon | ${PBM_TESTPREFIX}winicontopam | \
-  ${PBM_BINPREFIX}pamtopnm | cksum
-
-${PBM_BINPREFIX}pnmtile 32 32 testgrid.pbm | \
-${PBM_TESTPREFIX}pamtowinicon | ${PBM_TESTPREFIX}winicontopam | \
-  ${PBM_BINPREFIX}pamtopnm | ${PBM_BINPREFIX}ppmtopgm | \
-  ${PBM_BINPREFIX}pgmtopbm  -th -val=0.5 | cksum
+  alias pamtowinicon="${PBM_TESTPREFIX}pamtowinicon"
+  alias winicontopam="${PBM_TESTPREFIX}winicontopam"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  alias pgmtopbm="${PBM_BINPREFIX}pgmtopbm"
+  alias pnmcut="${PBM_BINPREFIX}pnmcut"
+  alias pnmtile="${PBM_BINPREFIX}pnmtile"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
+pnmcut --left=30 --width=48 --height=48 testimg.ppm | \
+pamtowinicon | winicontopam | \
+  pamtopnm | cksum
+
+pnmtile 32 32 testgrid.pbm | \
+pamtowinicon | winicontopam | \
+  pamtopnm | ppmtopgm | \
+  pgmtopbm  -th -val=0.5 | cksum
 
diff --git a/test/xbm-roundtrip.test b/test/xbm-roundtrip.test
index 2dcb5a13..4823adfd 100755
--- a/test/xbm-roundtrip.test
+++ b/test/xbm-roundtrip.test
@@ -2,5 +2,9 @@
 # This script tests: pbmtoxbm xbmtopbm
 # Also requires: 
 
-${PBM_TESTPREFIX}pbmtoxbm testgrid.pbm | ${PBM_TESTPREFIX}xbmtopbm | cksum
-${PBM_TESTPREFIX}pbmtoxbm -x10 testgrid.pbm | ${PBM_TESTPREFIX}xbmtopbm | cksum
+  alias pbmtoxbm="${PBM_TESTPREFIX}pbmtoxbm"
+  alias xbmtopbm="${PBM_TESTPREFIX}xbmtopbm"
+  shopt -s expand_aliases
+
+pbmtoxbm testgrid.pbm | xbmtopbm | cksum
+pbmtoxbm -x10 testgrid.pbm | xbmtopbm | cksum
diff --git a/test/xpm-roundtrip.test b/test/xpm-roundtrip.test
index 310022cc..c3c9894d 100755
--- a/test/xpm-roundtrip.test
+++ b/test/xpm-roundtrip.test
@@ -2,7 +2,13 @@
 # This script tests: ppmtoxpm xpmtoppm
 # Also requires: pgmtopbm ppmtopgm
 
-#${PBM_TESTPREFIX}ppmtoxpm -hexonly testimg.ppm | \
-# ${PBM_TESTPREFIX}xpmtoppm  | cksum
-${PBM_TESTPREFIX}ppmtoxpm testgrid.pbm | ${PBM_TESTPREFIX}xpmtoppm | \
-  ${PBM_BINPREFIX}ppmtopgm | ${PBM_BINPREFIX}pgmtopbm -th -value=0.5 | cksum
+  alias ppmtoxpm="${PBM_TESTPREFIX}ppmtoxpm"
+  alias xpmtoppm="${PBM_TESTPREFIX}xpmtoppm"
+  alias pgmtopbm="${PBM_BINPREFIX}pgmtopbm"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
+#ppmtoxpm -hexonly testimg.ppm | \
+# xpmtoppm  | cksum
+ppmtoxpm testgrid.pbm | xpmtoppm | \
+  ppmtopgm | pgmtopbm -th -value=0.5 | cksum
diff --git a/test/xwd-roundtrip.test b/test/xwd-roundtrip.test
index e24db7bc..b5614f01 100755
--- a/test/xwd-roundtrip.test
+++ b/test/xwd-roundtrip.test
@@ -2,13 +2,21 @@
 # This script tests: pnmtoxwd xwdtopnm
 # Also requires: pamdepth ppmtopgm
 
+  alias pnmtoxwd="${PBM_TESTPREFIX}pnmtoxwd"
+  alias xwdtopnm="${PBM_TESTPREFIX}xwdtopnm"
+  alias pamdepth="${PBM_BINPREFIX}pamdepth"
+  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
+  shopt -s expand_aliases
+
 # Test 1.  Should produce 2871603838 33838
 # which is the output of ppmtopgm testimg.ppm | cksum
-${PBM_BINPREFIX}ppmtopgm testimg.ppm | ${PBM_TESTPREFIX}pnmtoxwd  | \
-  ${PBM_TESTPREFIX}xwdtopnm | ${PBM_BINPREFIX}pamdepth 255 | cksum
+ppmtopgm testimg.ppm | pnmtoxwd  | \
+  xwdtopnm | pamdepth 255 | cksum
 
-${PBM_TESTPREFIX}pnmtoxwd --quiet  testimg.ppm | \
-  ${PBM_TESTPREFIX}xwdtopnm --quiet | ${PBM_BINPREFIX}pamdepth 255 | cksum
+# Test 2.  Should produce 1926073387 101484
+pnmtoxwd --quiet  testimg.ppm | \
+  xwdtopnm --quiet | pamdepth 255 | cksum
 
-${PBM_TESTPREFIX}pnmtoxwd --quiet  testgrid.pbm | \
-  ${PBM_TESTPREFIX}xwdtopnm | cksum
+# Test 3.  Should produce 2425386270 41
+pnmtoxwd --quiet  testgrid.pbm | \
+  xwdtopnm | cksum
diff --git a/test/yuv-roundtrip.test b/test/yuv-roundtrip.test
index 697d86aa..8e0d4f1a 100755
--- a/test/yuv-roundtrip.test
+++ b/test/yuv-roundtrip.test
@@ -2,8 +2,13 @@
 # This script tests: ppmtoyuv yuvtoppm
 # Also requires: pamgradient
 
+  alias ppmtoyuv="${PBM_TESTPREFIX}ppmtoyuv"
+  alias yuvtoppm="${PBM_TESTPREFIX}yuvtoppm"
+  alias pamgradient="${PBM_BINPREFIX}pamgradient"
+  shopt -s expand_aliases
+
 # Should produce 1904478375 253455
 
-${PBM_BINPREFIX}pamgradient rgb:00/ff/ff rgb:ff/ff/00 \
+pamgradient rgb:00/ff/ff rgb:ff/ff/00 \
                             rgb:ff/00/00 rgb:00/ff/00 352 240 | \
-    ${PBM_TESTPREFIX}ppmtoyuv | ${PBM_TESTPREFIX}yuvtoppm 352 240 | cksum
+    ppmtoyuv | yuvtoppm 352 240 | cksum
diff --git a/version.mk b/version.mk
index 682d7f1f..da0a87f5 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 65
-NETPBM_POINT_RELEASE = 7
+NETPBM_MINOR_RELEASE = 66
+NETPBM_POINT_RELEASE = 0