about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile2
-rw-r--r--buildtools/libopt.c2
-rw-r--r--converter/other/cameratopam/camera.c241
-rw-r--r--converter/other/cameratopam/camera.h95
-rw-r--r--converter/other/cameratopam/cameratopam.c1223
-rw-r--r--converter/other/cameratopam/cameratopam.h6
-rw-r--r--converter/other/cameratopam/canon.c6
-rw-r--r--converter/other/cameratopam/canon.h11
-rw-r--r--converter/other/cameratopam/dng.c151
-rw-r--r--converter/other/cameratopam/dng.h4
-rw-r--r--converter/other/cameratopam/foveon.c13
-rw-r--r--converter/other/cameratopam/foveon.h9
-rw-r--r--converter/other/cameratopam/global_variables.h1
-rw-r--r--converter/other/cameratopam/identify.c77
-rw-r--r--converter/other/cameratopam/identify.h4
-rw-r--r--converter/other/cameratopam/ljpeg.c5
-rw-r--r--converter/other/cameratopam/ljpeg.h5
-rw-r--r--converter/other/fiasco/config.h3
-rw-r--r--converter/other/fiasco/lib/misc.c16
-rw-r--r--converter/other/fiasco/lib/misc.h4
-rw-r--r--converter/other/giftopnm.c1
-rw-r--r--converter/other/ipdb.c1
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c9
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c10
-rw-r--r--converter/other/pamtopnm.c30
-rw-r--r--converter/other/pgmtoppm.c1
-rw-r--r--converter/other/pstopnm.c6
-rw-r--r--converter/other/svgtopam.c1
-rw-r--r--converter/other/tifftopnm.c114
-rw-r--r--converter/pbm/pbmtonokia.c1
-rw-r--r--converter/ppm/ppmtompeg/gethostname.c1
-rw-r--r--converter/ppm/ppmtompeg/mpeg.c1
-rw-r--r--converter/ppm/ppmtompeg/ppmtompeg.c1
-rw-r--r--converter/ppm/ppmtopict.c117
-rw-r--r--doc/HISTORY66
-rw-r--r--doc/INSTALL28
-rw-r--r--doc/USERDOC2
-rw-r--r--editor/Makefile6
-rw-r--r--editor/pamperspective.c1
-rw-r--r--editor/pamundice.c11
-rw-r--r--editor/pnmcomp.c460
-rw-r--r--editor/pnmconvol.c425
-rw-r--r--editor/pnmmontage.c1
-rw-r--r--editor/ppmcolormask.c1
-rw-r--r--editor/ppmdraw.c5
-rw-r--r--editor/specialty/pnmindex.c1
-rw-r--r--generator/Makefile7
-rw-r--r--generator/pamcrater.c428
-rw-r--r--generator/pamshadedrelief.c250
-rwxr-xr-xgenerator/pgmcrater94
-rw-r--r--generator/pgmcrater.c423
-rw-r--r--lib/colorname.c6
-rw-r--r--lib/libpam.c2
-rw-r--r--lib/libpamcolor.c2
-rw-r--r--lib/libpm.c93
-rw-r--r--lib/libppmcolor.c2
-rw-r--r--lib/pmfileio.c3
-rw-r--r--lib/util/nstring.c2
-rw-r--r--other/pamx/pamx.c1
-rw-r--r--other/pamx/window.c1
-rw-r--r--test/Test-Order10
-rw-r--r--test/all-in-place.ok2
-rwxr-xr-xtest/all-in-place.test13
-rw-r--r--test/atari-roundtrip.ok3
-rwxr-xr-xtest/atari-roundtrip.test40
-rw-r--r--test/cis-roundtrip.ok2
-rwxr-xr-xtest/cis-roundtrip.test20
-rw-r--r--test/facesaver-roundtrip.ok2
-rwxr-xr-xtest/facesaver-roundtrip.test7
-rwxr-xr-xtest/gif-quant-roundtrip.test11
-rw-r--r--test/gif-roundtrip.ok8
-rwxr-xr-xtest/gif-roundtrip.test62
-rw-r--r--test/ilbm-roundtrip.ok1
-rwxr-xr-xtest/ilbm-roundtrip.test20
-rw-r--r--test/jbig-roundtrip.ok2
-rwxr-xr-xtest/jbig-roundtrip.test8
-rw-r--r--test/macp-roundtrip.ok2
-rwxr-xr-xtest/macp-roundtrip.test15
-rw-r--r--test/mda-roundtrip.ok2
-rwxr-xr-xtest/mda-roundtrip.test19
-rwxr-xr-xtest/pamchannel.test5
-rw-r--r--test/pamcrater.ok6
-rwxr-xr-xtest/pamcrater.test53
-rwxr-xr-xtest/pamdice-roundtrip.test9
-rw-r--r--test/pamditherbw.ok8
-rwxr-xr-xtest/pamditherbw.test27
-rw-r--r--test/pamenlarge.ok2
-rwxr-xr-xtest/pamenlarge.test8
-rw-r--r--test/pamfile.ok2
-rwxr-xr-xtest/pamfile.test9
-rwxr-xr-x[-rw-r--r--]test/pamflip1.test0
-rwxr-xr-x[-rw-r--r--]test/pamflip2.test0
-rwxr-xr-xtest/pamslice-roundtrip.test26
-rwxr-xr-xtest/pbmclean.test10
-rw-r--r--test/pcx-roundtrip.ok5
-rwxr-xr-xtest/pcx-roundtrip.test49
-rw-r--r--test/pgmbentley.ok1
-rwxr-xr-xtest/pgmbentley.test10
-rwxr-xr-xtest/pgmtoppm.test17
-rw-r--r--test/pi3-roundtrip.ok2
-rwxr-xr-xtest/pi3-roundtrip.test22
-rw-r--r--test/pict-roundtrip.ok1
-rwxr-xr-xtest/pict-roundtrip.test15
-rw-r--r--test/pnminvert.ok2
-rwxr-xr-xtest/pnminvert.test18
-rwxr-xr-xtest/pnmpsnr.test18
-rwxr-xr-xtest/pnmremap1.test16
-rwxr-xr-xtest/pnmremap2.test17
-rwxr-xr-xtest/pnmtile.test18
-rwxr-xr-xtest/ppmcie.test16
-rwxr-xr-xtest/ppmdim.test13
-rwxr-xr-xtest/ppmmix.test23
-rwxr-xr-xtest/ps-alt-roundtrip.test33
-rwxr-xr-xtest/ps-roundtrip.test53
-rwxr-xr-xtest/rgb3-roundtrip.test39
-rw-r--r--test/sgi-roundtrip.ok6
-rwxr-xr-xtest/sgi-roundtrip.test38
-rw-r--r--test/targa-roundtrip.ok2
-rwxr-xr-xtest/targa-roundtrip.test13
-rw-r--r--test/utahrle-roundtrip.ok2
-rwxr-xr-xtest/utahrle-roundtrip.test12
-rw-r--r--test/xwd-roundtrip.ok2
-rwxr-xr-xtest/xwd-roundtrip.test10
-rw-r--r--version.mk4
124 files changed, 3022 insertions, 2362 deletions
diff --git a/GNUmakefile b/GNUmakefile
index c4f0ad31..4be3c7c8 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -148,7 +148,7 @@ pm_config.h: \
 	echo '#ifndef PM_CONFIG_H' >>$@
 	echo '#define PM_CONFIG_H' >>$@
 ifeq ($(INTTYPES_H)x,x)
-	echo '/* Don't need to #include any inttypes.h-type thing */
+	echo '/* Dont need to #include any inttypes.h-type thing */' >>$@
 else
   ifeq ($(INTTYPES_H),"inttypes_netpbm.h")
 	cat inttypes_netpbm.h >>$@
diff --git a/buildtools/libopt.c b/buildtools/libopt.c
index 04b8cec6..a0bf1cda 100644
--- a/buildtools/libopt.c
+++ b/buildtools/libopt.c
@@ -66,7 +66,7 @@
 
 -----------------------------------------------------------------------------*/
 #define _BSD_SOURCE 1      /* Make sure strdup() is in stdio.h */
-#define _XOPEN_SOURCE 600  /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #define MAX_PREFIXES 10
 
diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c
index 4008dbb2..14d208e3 100644
--- a/converter/other/cameratopam/camera.c
+++ b/converter/other/cameratopam/camera.c
@@ -17,6 +17,7 @@
 #include "mallocvar.h"
 
 #include "global_variables.h"
+#include "cameratopam.h"
 #include "util.h"
 #include "decode.h"
 #include "bayer.h"
@@ -55,67 +56,76 @@ merror (const void *ptr, const char *where)
 
 
 static void  
-adobe_copy_pixel (int row, int col, unsigned short **rp, bool use_secondary)
-{
-  unsigned r=row, c=col;
-
-  if (fuji_secondary && use_secondary) (*rp)++;
-  if (filters) {
-    if (fuji_width) {
-      r = row + fuji_width - 1 - (col >> 1);
-      c = row + ((col+1) >> 1);
-    }
-    if (r < height && c < width)
-      BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
-    *rp += 1 + fuji_secondary;
-  } else
-    for (c=0; c < tiff_samples; c++) {
-      image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp;
-      (*rp)++;
+adobeCopyPixel(Image             const image,
+               unsigned int      const row,
+               unsigned int      const col,
+               unsigned short ** const rp,
+               bool              const useSecondary) {
+
+    unsigned r=row, c=col;
+
+    if (fuji_secondary && useSecondary)
+        ++(*rp);
+    if (filters) {
+        if (fuji_width) {
+            r = row + fuji_width - 1 - (col >> 1);
+            c = row + ((col+1) >> 1);
+        }
+        if (r < height && c < width)
+            BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
+        *rp += 1 + fuji_secondary;
+    } else {
+        unsigned int c;
+        for (c = 0; c < tiff_samples; ++c) {
+            image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp;
+            ++(*rp);
+        }
     }
-  if (fuji_secondary && use_secondary) (*rp)--;
+    if (fuji_secondary && useSecondary)
+        --(*rp);
 }
 
 void
-adobe_dng_load_raw_lj()
-{
-  int save, twide, trow=0, tcol=0, jrow, jcol;
-  struct jhead jh;
-  unsigned short *rp;
+adobe_dng_load_raw_lj(Image const image) {
 
-  while (1) {
-    save = ftell(ifp);
-    fseek (ifp, get4(ifp), SEEK_SET);
-    if (!ljpeg_start (ifp, &jh)) break;
-    if (trow >= raw_height) break;
-    if (jh.high > raw_height-trow)
-    jh.high = raw_height-trow;
-    twide = jh.wide;
-    if (filters) twide *= jh.clrs;
-    else         colors = jh.clrs;
-    if (fuji_secondary) twide /= 2;
-    if (twide > raw_width-tcol)
-    twide = raw_width-tcol;
-
-    for (jrow=0; jrow < jh.high; jrow++) {
-      ljpeg_row (&jh);
-      for (rp=jh.row, jcol=0; jcol < twide; jcol++)
-    adobe_copy_pixel (trow+jrow, tcol+jcol, &rp, use_secondary);
-    }
-    fseek (ifp, save+4, SEEK_SET);
-    if ((tcol += twide) >= raw_width) {
-      tcol = 0;
-      trow += jh.high;
+    int save, twide, trow=0, tcol=0, jrow, jcol;
+    struct jhead jh;
+    unsigned short *rp;
+
+    while (1) {
+        save = ftell(ifp);
+        fseek (ifp, get4(ifp), SEEK_SET);
+        if (!ljpeg_start (ifp, &jh)) break;
+        if (trow >= raw_height) break;
+        if (jh.high > raw_height-trow)
+            jh.high = raw_height-trow;
+        twide = jh.wide;
+        if (filters) twide *= jh.clrs;
+        else         colors = jh.clrs;
+        if (fuji_secondary) twide /= 2;
+        if (twide > raw_width-tcol)
+            twide = raw_width-tcol;
+
+        for (jrow=0; jrow < jh.high; jrow++) {
+            ljpeg_row (&jh);
+            for (rp=jh.row, jcol=0; jcol < twide; jcol++)
+                adobeCopyPixel(image,
+                               trow+jrow, tcol+jcol, &rp, use_secondary);
+        }
+        fseek (ifp, save+4, SEEK_SET);
+        if ((tcol += twide) >= raw_width) {
+            tcol = 0;
+            trow += jh.high;
+        }
+        free (jh.row);
     }
-    free (jh.row);
-  }
 }
 
 
 
 void
-adobe_dng_load_raw_nc()
-{
+adobe_dng_load_raw_nc(Image const image) {
+
     unsigned short *pixel, *rp;
     int row, col;
 
@@ -124,7 +134,7 @@ adobe_dng_load_raw_nc()
     for (row=0; row < raw_height; row++) {
         read_shorts (ifp, pixel, raw_width * tiff_samples);
         for (rp=pixel, col=0; col < raw_width; col++)
-            adobe_copy_pixel (row, col, &rp, use_secondary);
+            adobeCopyPixel(image, row, col, &rp, use_secondary);
     }
     free (pixel);
 }
@@ -134,8 +144,8 @@ adobe_dng_load_raw_nc()
 static int nikon_curve_offset;
 
 void
-nikon_compressed_load_raw(void)
-{
+nikon_compressed_load_raw(Image const image) {
+
     static const unsigned char nikon_tree[] = {
         0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,
         5,4,3,6,2,7,1,0,8,9,11,10,12
@@ -176,8 +186,8 @@ nikon_compressed_load_raw(void)
 }
 
 void
-nikon_load_raw()
-{
+nikon_load_raw(Image const image) {
+
   int irow, row, col, i;
 
   getbits(ifp, -1);
@@ -294,8 +304,8 @@ minolta_z2()
 }
 
 void
-nikon_e2100_load_raw()
-{
+nikon_e2100_load_raw(Image const image) {
+        
   unsigned char   data[3432], *dp;
   unsigned short pixel[2288], *pix;
   int row, col;
@@ -322,8 +332,8 @@ nikon_e2100_load_raw()
 }
 
 void
-nikon_e950_load_raw()
-{
+nikon_e950_load_raw(Image const image) {
+
   int irow, row, col;
 
   getbits(ifp, -1);
@@ -341,8 +351,7 @@ nikon_e950_load_raw()
    The Fuji Super CCD is just a Bayer grid rotated 45 degrees.
  */
 void
-fuji_s2_load_raw()
-{
+fuji_s2_load_raw(Image const image) {
   unsigned short pixel[2944];
   int row, col, r, c;
 
@@ -358,8 +367,7 @@ fuji_s2_load_raw()
 }
 
 void
-fuji_s3_load_raw()
-{
+fuji_s3_load_raw(Image const image) {
   unsigned short pixel[4352];
   int row, col, r, c;
 
@@ -374,42 +382,52 @@ fuji_s3_load_raw()
   }
 }
 
-static void  fuji_common_load_raw (int ncol, int icol, int nrow)
-{
-  unsigned short pixel[2048];
-  int row, col, r, c;
-
-  for (row=0; row < nrow; row++) {
-    read_shorts(ifp, pixel, ncol);
-    for (col=0; col <= icol; col++) {
-      r = icol - col + (row >> 1);
-      c = col + ((row+1) >> 1);
-      BAYER(r,c) = pixel[col];
+static void 
+fuji_common_load_raw(Image        const image,
+                     unsigned int const ncol,
+                     unsigned int const icol,
+                     unsigned int const nrow) {
+
+    unsigned short pixel[2048];
+    unsigned int row;
+
+    for (row = 0; row < nrow; ++row) {
+        unsigned int col;
+        read_shorts(ifp, pixel, ncol);
+        for (col = 0; col <= icol; ++col) {
+            int const r = icol - col + (row >> 1);
+            int const c = col + ((row+1) >> 1);
+            BAYER(r,c) = pixel[col];
+        }
     }
-  }
 }
 
+
+
 void
-fuji_s5000_load_raw()
-{
+fuji_s5000_load_raw(Image const image) {
+
   fseek (ifp, (1472*4+24)*2, SEEK_CUR);
-  fuji_common_load_raw (1472, 1423, 2152);
+  fuji_common_load_raw(image, 1472, 1423, 2152);
 }
 
+
+
 void
-fuji_s7000_load_raw()
-{
-  fuji_common_load_raw (2048, 2047, 3080);
+fuji_s7000_load_raw(Image const image) {
+
+    fuji_common_load_raw(image, 2048, 2047, 3080);
 }
 
+
+
 /*
    The Fuji Super CCD SR has two photodiodes for each pixel.
    The secondary has about 1/16 the sensitivity of the primary,
    but this ratio may vary.
  */
 void
-fuji_f700_load_raw()
-{
+fuji_f700_load_raw(Image const image) {
   unsigned short pixel[2944];
   int row, col, r, c, val;
 
@@ -425,8 +443,7 @@ fuji_f700_load_raw()
 }
 
 void
-rollei_load_raw()
-{
+rollei_load_raw(Image const image) {
   unsigned char pixel[10];
   unsigned iten=0, isix, i, buffer=0, row, col, todo[16];
 
@@ -452,8 +469,7 @@ rollei_load_raw()
 }
 
 void
-phase_one_load_raw()
-{
+phase_one_load_raw(Image const image) {
   int row, col, a, b;
   unsigned short *pixel, akey, bkey;
 
@@ -479,8 +495,7 @@ phase_one_load_raw()
 }
 
 void
-ixpress_load_raw()
-{
+ixpress_load_raw(Image const image) {
   unsigned short pixel[4090];
   int row, col;
 
@@ -494,8 +509,7 @@ ixpress_load_raw()
 }
 
 void
-leaf_load_raw()
-{
+leaf_load_raw(Image const image) {
   unsigned short *pixel;
   int r, c, row, col;
 
@@ -514,8 +528,7 @@ leaf_load_raw()
    For this function only, raw_width is in bytes, not pixels!
  */
 void
-packed_12_load_raw()
-{
+packed_12_load_raw(Image const image) {
   int row, col;
 
   getbits(ifp, -1);
@@ -528,8 +541,7 @@ packed_12_load_raw()
 }
 
 void
-unpacked_load_raw()
-{
+unpacked_load_raw(Image const image) {
   unsigned short *pixel;
   int row, col;
 
@@ -544,8 +556,7 @@ unpacked_load_raw()
 }
 
 void
-olympus_e300_load_raw()
-{
+olympus_e300_load_raw(Image const image) {
   unsigned char  *data,  *dp;
   unsigned short *pixel, *pix;
   int dwide, row, col;
@@ -568,8 +579,7 @@ olympus_e300_load_raw()
 }
 
 void
-olympus_cseries_load_raw()
-{
+olympus_cseries_load_raw(Image const image) {
   int irow, row, col;
 
   for (irow=0; irow < height; irow++) {
@@ -584,8 +594,7 @@ olympus_cseries_load_raw()
 }
 
 void
-eight_bit_load_raw()
-{
+eight_bit_load_raw(Image const image) {
   unsigned char *pixel;
   int row, col;
 
@@ -601,8 +610,7 @@ eight_bit_load_raw()
 }
 
 void
-casio_qv5700_load_raw()
-{
+casio_qv5700_load_raw(Image const image) {
   unsigned char  data[3232],  *dp;
   unsigned short pixel[2576], *pix;
   int row, col;
@@ -622,8 +630,7 @@ casio_qv5700_load_raw()
 }
 
 void
-nucore_load_raw()
-{
+nucore_load_raw(Image const image) {
   unsigned short *pixel;
   int irow, row, col;
 
@@ -685,8 +692,7 @@ static int  radc_token (int tree)
 : (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4)
 
 void
-kodak_radc_load_raw()
-{
+kodak_radc_load_raw(Image const image) {
     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];
@@ -765,7 +771,8 @@ kodak_radc_load_raw()
 #undef PREDICTOR
 
 #ifndef HAVE_JPEG
-void kodak_jpeg_load_raw() {}
+void
+kodak_jpeg_load_raw(Image const Image) {}
 #else
 
 static bool
@@ -782,7 +789,7 @@ fill_input_buffer (j_decompress_ptr cinfo)
 }
 
 void
-kodak_jpeg_load_raw()
+kodak_jpeg_load_raw(Image const image)
 {
   struct jpeg_decompress_struct cinfo;
   struct jpeg_error_mgr jerr;
@@ -823,7 +830,7 @@ kodak_jpeg_load_raw()
 #endif
 
 void
-kodak_dc120_load_raw()
+kodak_dc120_load_raw(Image const image)
 {
   static const int mul[4] = { 162, 192, 187,  92 };
   static const int add[4] = {   0, 636, 424, 212 };
@@ -859,7 +866,7 @@ kodak_dc20_coeff (float const juice)
 }
 
 void
-kodak_easy_load_raw()
+kodak_easy_load_raw(Image const image)
 {
   unsigned char *pixel;
   unsigned row, col, icol;
@@ -887,7 +894,7 @@ kodak_easy_load_raw()
 }
 
 void
-kodak_compressed_load_raw()
+kodak_compressed_load_raw(Image const image)
 {
   unsigned char c, blen[256];
   unsigned short raw[6];
@@ -951,7 +958,7 @@ kodak_compressed_load_raw()
 }
 
 void
-kodak_yuv_load_raw()
+kodak_yuv_load_raw(Image const image)
 {
   unsigned char c, blen[384];
   unsigned row, col, len, bits=0;
@@ -1042,7 +1049,7 @@ static void  sony_decrypt (unsigned *data, int len, int start, int key)
 }
 
 void
-sony_load_raw()
+sony_load_raw(Image const image)
 {
   unsigned char head[40];
   struct pixel {
@@ -1310,12 +1317,6 @@ parse_mos(FILE * const ifp,
         fread (data, 1, 40, ifp);
         skip = get4(ifp);
         from = ftell(ifp);
-#ifdef USE_LCMS
-        if (!strcmp(data,"icc_camera_profile")) {
-            profile_length = skip;
-            profile_offset = from;
-        }
-#endif
         if (!strcmp(data,"NeutObj_neutrals")) {
             for (i=0; i < 4; i++)
                 fscanf (ifp, "%d", neut+i);
diff --git a/converter/other/cameratopam/camera.h b/converter/other/cameratopam/camera.h
index a1e884cf..02c3f2af 100644
--- a/converter/other/cameratopam/camera.h
+++ b/converter/other/cameratopam/camera.h
@@ -1,5 +1,10 @@
+#ifndef CAMERA_H_INCLUDED
+#define CAMERA_H_INCLUDED
+
 #include <stdio.h>
 
+#include "cameratopam.h"
+
 void 
 parse_ciff(FILE * const ifp,
            int    const offset,
@@ -21,20 +26,18 @@ void
 parse_mos(FILE * const ifp,
           int    const offset);
 
-void
-adobe_dng_load_raw_lj(void);
+typedef void LoadRawFn(Image const image);
 
-void
-adobe_dng_load_raw_nc(void);
+LoadRawFn adobe_dng_load_raw_lj;
+
+LoadRawFn adobe_dng_load_raw_nc;
 
 int
 nikon_is_compressed(void);
 
-void
-nikon_compressed_load_raw(void);
+LoadRawFn nikon_compressed_load_raw;
 
-void
-nikon_e950_load_raw(void);
+LoadRawFn nikon_e950_load_raw;
 
 void
 nikon_e950_coeff(void);
@@ -45,87 +48,63 @@ nikon_e990(void);
 int
 nikon_e2100(void);
 
-void
-nikon_e2100_load_raw(void);
+LoadRawFn nikon_e2100_load_raw;
 
 int
 minolta_z2(void);
 
-void
-fuji_s2_load_raw(void);
+LoadRawFn fuji_s2_load_raw;
 
-void
-fuji_s3_load_raw(void);
+LoadRawFn fuji_s3_load_raw;
 
-void
-fuji_s5000_load_raw(void);
+LoadRawFn fuji_s5000_load_raw;
 
-void
-unpacked_load_raw(void);
+LoadRawFn unpacked_load_raw;
 
-void
-fuji_s7000_load_raw(void);
+LoadRawFn fuji_s7000_load_raw;
 
-void
-fuji_f700_load_raw(void);
+LoadRawFn fuji_f700_load_raw;
 
-void
-packed_12_load_raw(void);
+LoadRawFn packed_12_load_raw;
 
-void
-eight_bit_load_raw(void);
+LoadRawFn eight_bit_load_raw;
 
-void
-phase_one_load_raw(void);
+LoadRawFn phase_one_load_raw;
 
-void
-ixpress_load_raw(void);
+LoadRawFn ixpress_load_raw;
 
-void
-leaf_load_raw(void);
+LoadRawFn leaf_load_raw;
 
-void
-olympus_e300_load_raw(void);
+LoadRawFn olympus_e300_load_raw;
 
-void
-olympus_cseries_load_raw(void);
+LoadRawFn olympus_cseries_load_raw;
 
-void
-sony_load_raw(void);
+LoadRawFn sony_load_raw;
 
-void
-kodak_easy_load_raw(void);
+LoadRawFn kodak_easy_load_raw;
 
-void
-kodak_compressed_load_raw(void);
+LoadRawFn kodak_compressed_load_raw;
 
-void
-kodak_yuv_load_raw(void);
+LoadRawFn kodak_yuv_load_raw;
 
 void
 kodak_dc20_coeff (float const juice);
 
-void
-kodak_radc_load_raw(void);
+LoadRawFn kodak_radc_load_raw;
 
-void
-kodak_jpeg_load_raw(void);
+LoadRawFn kodak_jpeg_load_raw;
 
-void
-kodak_dc120_load_raw(void);
+LoadRawFn kodak_dc120_load_raw;
 
-void
-rollei_load_raw(void);
+LoadRawFn rollei_load_raw;
 
-void
-casio_qv5700_load_raw(void);
+LoadRawFn casio_qv5700_load_raw;
 
-void
-nucore_load_raw(void);
+LoadRawFn nucore_load_raw;
 
-void
-nikon_load_raw(void);
+LoadRawFn nikon_load_raw;
 
 int
 pentax_optio33(void);
 
+#endif
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
index 71c9c7af..40d8207f 100644
--- a/converter/other/cameratopam/cameratopam.c
+++ b/converter/other/cameratopam/cameratopam.c
@@ -8,7 +8,8 @@
 
 
 #define _BSD_SOURCE 1   /* Make sure string.h contains strdup() */
-#define _XOPEN_SOURCE  /* Make sure unistd.h contains swab() */
+#define _XOPEN_SOURCE 500
+   /* Make sure unistd.h contains swab(), string.h constains strdup() */
 
 #include "pm_config.h"
 
@@ -38,6 +39,7 @@
 #include "pam.h"
 
 #include "global_variables.h"
+#include "cameratopam.h"
 #include "util.h"
 #include "decode.h"
 #include "identify.h"
@@ -61,22 +63,19 @@ int height, width, fuji_width, colors, tiff_samples;
 int black, maximum, clip_max;
 int iheight, iwidth, shrink;
 int is_dng, is_canon, is_foveon, use_coeff, use_gamma;
-int trim, flip, xmag, ymag;
+int flip, xmag, ymag;
 int zero_after_ff;
 unsigned filters;
-unsigned short (*image)[4], white[8][8], curve[0x1000];
+unsigned short  white[8][8];
+unsigned short  curve[0x1000];
 int fuji_secondary;
-float cam_mul[4], pre_mul[4], coeff[3][4];
+float cam_mul[4], coeff[3][4];
+float pre_mul[4];
 int histogram[3][0x2000];
 jmp_buf failure;
 int use_secondary;
 bool verbose;
 
-#ifdef USE_LCMS
-#include <lcms.h>
-int profile_offset, profile_length;
-#endif
-
 #define CLASS
 
 #define FORC3 for (c=0; c < 3; c++)
@@ -88,7 +87,7 @@ static void CLASS merror (const void *ptr, const char *where)
         pm_error ("Out of memory in %s", where);
 }
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -112,11 +111,11 @@ struct cmdlineInfo {
 };
 
 
-static struct cmdlineInfo cmdline;
+static struct CmdlineInfo cmdline;
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+                 struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -191,615 +190,694 @@ parseCommandLine(int argc, char ** argv,
 }
 
   
-/*
-   Seach from the current directory up to the root looking for
-   a ".badpixels" file, and fix those pixels now.
- */
-static void CLASS bad_pixels()
-{
-  FILE *fp=NULL;
-  char *fname, *cp, line[128];
-  int len, time, row, col, r, c, rad, tot, n, fixed=0;
-
-  if (!filters) return;
-  for (len=16 ; ; len *= 2) {
-    fname = malloc (len);
-    if (!fname) return;
-    if (getcwd (fname, len-12)) break;
-    free (fname);
-    if (errno != ERANGE) return;
-  }
+
+static void CLASS
+fixBadPixels(Image const image) {
+/*----------------------------------------------------------------------------
+  Search from the current directory up to the root looking for
+  a ".badpixels" file, and fix those pixels now.
+-----------------------------------------------------------------------------*/
+    if (filters) {
+        FILE *fp;
+        char *fname, *cp, line[128];
+        int len, time, row, col, rad, tot, n, fixed=0;
+
+        for (len=16 ; ; len *= 2) {
+            fname = malloc (len);
+            if (!fname) return;
+            if (getcwd (fname, len-12))
+                break;
+            free (fname);
+            if (errno != ERANGE)
+                return;
+        }
 #if MSVCRT
-  if (fname[1] == ':')
-    memmove (fname, fname+2, len-2);
-  for (cp=fname; *cp; cp++)
-    if (*cp == '\\') *cp = '/';
+        if (fname[1] == ':')
+            memmove (fname, fname+2, len-2);
+        for (cp=fname; *cp; cp++)
+            if (*cp == '\\') *cp = '/';
 #endif
-  cp = fname + strlen(fname);
-  if (cp[-1] == '/') cp--;
-  while (*fname == '/') {
-    strcpy (cp, "/.badpixels");
-    if ((fp = fopen (fname, "r"))) break;
-    if (cp == fname) break;
-    while (*--cp != '/');
-  }
-  free (fname);
-  if (!fp) return;
-  while (fgets (line, 128, fp)) {
-    cp = strchr (line, '#');
-    if (cp) *cp = 0;
-    if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue;
-    if ((unsigned) col >= width || (unsigned) row >= height) continue;
-    if (time > timestamp) continue;
-    for (tot=n=0, rad=1; rad < 3 && n==0; rad++)
-      for (r = row-rad; r <= row+rad; r++)
-    for (c = col-rad; c <= col+rad; c++)
-      if ((unsigned) r < height && (unsigned) c < width &&
-        (r != row || c != col) && FC(r,c) == FC(row,col)) {
-        tot += BAYER(r,c);
-        n++;
-      }
-    BAYER(row,col) = tot/n;
-    if (cmdline.verbose) {
-      if (!fixed++)
-          pm_message ("Fixed bad pixels at: %d,%d", col, row);
+        cp = fname + strlen(fname);
+        if (cp[-1] == '/')
+            --cp;
+        fp = NULL; /* initial value */
+        while (*fname == '/') {
+            strcpy (cp, "/.badpixels");
+            fp = fopen (fname, "r");
+            if (fp)
+                break;
+            if (cp == fname)
+                break;
+            while (*--cp != '/');
+        }
+        free (fname);
+        if (fp) {
+            while (fgets (line, 128, fp)) {
+                char * cp;
+                cp = strchr (line, '#');
+                if (cp) *cp = 0;
+                if (sscanf (line, "%d %d %d", &col, &row, &time) != 3)
+                    continue;
+                if ((unsigned) col >= width || (unsigned) row >= height)
+                    continue;
+                if (time > timestamp) continue;
+                for (tot=n=0, rad=1; rad < 3 && n==0; rad++) {
+                    unsigned int r;
+                    for (r = row-rad; r <= row+rad; ++r) {
+                        unsigned int c;
+                        for (c = col-rad; c <= col+rad; ++c) {
+                            if ((unsigned) r < height &&
+                                (unsigned) c < width  &&
+                                (r != row || c != col) &&
+                                FC(r,c) == FC(row,col)) {
+                                tot += BAYER(r,c);
+                                ++n;
+                            }
+                        }
+                    }
+                }
+                BAYER(row,col) = tot/n;
+                if (cmdline.verbose) {
+                    if (!fixed++)
+                        pm_message ("Fixed bad pixels at: %d,%d", col, row);
+                }
+            }
+            fclose (fp);
+        }
     }
-  }
-  fclose (fp);
 }
 
-static void CLASS scale_colors()
-{
-  int row, col, c, val, shift=0;
-  int min[4], max[4], count[4];
-  double sum[4], dmin;
-
-  maximum -= black;
-  if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) {
-    FORC4 min[c] = INT_MAX;
-    FORC4 max[c] = count[c] = sum[c] = 0;
-    for (row=0; row < height; row++)
-      for (col=0; col < width; col++)
-    FORC4 {
-      val = image[row*width+col][c];
-      if (!val) continue;
-      if (min[c] > val) min[c] = val;
-      if (max[c] < val) max[c] = val;
-      val -= black;
-      if (val > maximum-25) continue;
-      if (val < 0) val = 0;
-      sum[c] += val;
-      count[c]++;
+
+
+static void CLASS
+scaleColors(Image const image) {
+
+    int row;
+    int c;
+    int val;
+    int shift;
+    int min[4], max[4], count[4];
+    double sum[4], dmin;
+    int scaleMax;
+
+    scaleMax = maximum - black;  /* initial value */
+    if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) {
+        unsigned int row;
+        FORC4 min  [c] = INT_MAX;
+        FORC4 max  [c] = 0;
+        FORC4 count[c] = 0;
+        FORC4 sum  [c] = 0;
+        for (row = 0; row < height; ++row) {
+            unsigned int col;
+            for (col = 0; col < width; ++col) {
+                FORC4 {
+                    int val;
+                    val = image[row*width+col][c];
+                    if (val != 0) {
+                        if (min[c] > val)
+                            min[c] = val;
+                        if (max[c] < val)
+                            max[c] = val;
+                        val -= black;
+                        if (val <= scaleMax-25) {
+                            sum  [c] += MAX(0, val);
+                            count[c] += 1;
+                        }
+                    }
+                }
+            }
+        }
+        FORC4 pre_mul[c] = count[c] / sum[c];
     }
-    FORC4 pre_mul[c] = count[c] / sum[c];
-  }
-  if (cmdline.use_camera_wb && camera_red != -1) {
-    FORC4 count[c] = sum[c] = 0;
-    for (row=0; row < 8; row++)
-      for (col=0; col < 8; col++) {
-    c = FC(row,col);
-    if ((val = white[row][col] - black) > 0)
-      sum[c] += val;
-    count[c]++;
-      }
-    val = 1;
-    FORC4 if (sum[c] == 0) val = 0;
-    if (val)
-      FORC4 pre_mul[c] = count[c] / sum[c];
-    else if (camera_red && camera_blue)
-      memcpy (pre_mul, cam_mul, sizeof pre_mul);
-    else
-      pm_message ("Cannot use camera white balance.");
-  }
-  if (!use_coeff) {
-    pre_mul[0] *= cmdline.red_scale;
-    pre_mul[2] *= cmdline.blue_scale;
-  }
-  dmin = DBL_MAX;
-  FORC4 if (dmin > pre_mul[c])
+    if (cmdline.use_camera_wb && camera_red != -1) {
+        unsigned int row;
+        FORC4 count[c] = sum[c] = 0;
+        for (row = 0; row < 8; ++row) {
+            unsigned int col;
+            for (col = 0; col < 8; ++col) {
+                c = FC(row,col);
+                if ((val = white[row][col] - black) > 0)
+                    sum[c] += val;
+                ++count[c];
+            }
+        }
+        val = 1;
+        FORC4 if (sum[c] == 0) val = 0;
+        if (val)
+            FORC4 pre_mul[c] = count[c] / sum[c];
+        else if (camera_red && camera_blue)
+            memcpy(pre_mul, cam_mul, sizeof pre_mul);
+        else
+            pm_message ("Cannot use camera white balance.");
+    }
+    if (!use_coeff) {
+        pre_mul[0] *= cmdline.red_scale;
+        pre_mul[2] *= cmdline.blue_scale;
+    }
+    dmin = DBL_MAX;
+    FORC4 if (dmin > pre_mul[c])
         dmin = pre_mul[c];
-  FORC4 pre_mul[c] /= dmin;
-
-  while (maximum << shift < 0x8000) shift++;
-  FORC4 pre_mul[c] *= 1 << shift;
-  maximum <<= shift;
-
-  if (cmdline.linear || cmdline.bright < 1) {
-      maximum *= cmdline.bright;
-      if (maximum > 0xffff)
-          maximum = 0xffff;
-      FORC4 pre_mul[c] *= cmdline.bright;
-  }
-  if (cmdline.verbose) {
-    fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black);
-    FORC4 fprintf (stderr, " %f", pre_mul[c]);
-    fputc ('\n', stderr);
-  }
-  clip_max = cmdline.no_clip_color ? 0xffff : maximum;
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      FORC4 {
-    val = image[row*width+col][c];
-    if (!val) continue;
-    val -= black;
-    val *= pre_mul[c];
-    if (val < 0) val = 0;
-    if (val > clip_max) val = clip_max;
-    image[row*width+col][c] = val;
-      }
-}
-
-/*
-   This algorithm is officially called:
+    FORC4 pre_mul[c] /= dmin;
 
-   "Interpolation using a Threshold-based variable number of gradients"
+    for (shift = 0; scaleMax << shift < 0x8000; ++shift);
 
-   described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html
+    FORC4 pre_mul[c] *= 1 << shift;
+    scaleMax <<= shift;
 
-   I've extended the basic idea to work with non-Bayer filter arrays.
-   Gradients are numbered clockwise from NW=0 to W=7.
- */
-static void CLASS vng_interpolate()
-{
-  static const signed char *cp, terms[] = {
-    -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01,
-    -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01,
-    -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03,
-    -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06,
-    -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04,
-    -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01,
-    -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40,
-    -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11,
-    -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11,
-    -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22,
-    -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44,
-    -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10,
-    -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04,
-    +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40,
-    +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20,
-    +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08,
-    +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20,
-    +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44,
-    +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60,
-    +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80,
-    +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40,
-    +1,+0,+2,+1,0,(char)0x10
-  }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
-  unsigned short (*brow[5])[4], *pix;
-  int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4];
-  int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
-  int g, diff, thold, num, c;
-
-  for (row=0; row < 8; row++) {     /* Precalculate for bilinear */
-    for (col=1; col < 3; col++) {
-      ip = code[row][col & 1];
-      memset (sum, 0, sizeof sum);
-      for (y=-1; y <= 1; y++)
-    for (x=-1; x <= 1; x++) {
-      shift = (y==0) + (x==0);
-      if (shift == 2) continue;
-      color = FC(row+y,col+x);
-      *ip++ = (width*y + x)*4 + color;
-      *ip++ = shift;
-      *ip++ = color;
-      sum[color] += 1 << shift;
-    }
-      FORC4
-    if (c != FC(row,col)) {
-      *ip++ = c;
-      *ip++ = sum[c];
+    if (cmdline.linear || cmdline.bright < 1) {
+        scaleMax = MIN(0xffff, scaleMax * cmdline.bright);
+        FORC4 pre_mul[c] *= cmdline.bright;
     }
+    if (cmdline.verbose) {
+        fprintf(stderr, "Scaling with black=%d, ", black);
+        fprintf(stderr, "pre_mul[] = ");
+        FORC4 fprintf (stderr, " %f", pre_mul[c]);
+        fprintf(stderr, "\n");
     }
-  }
-  for (row=1; row < height-1; row++) {  /* Do bilinear interpolation */
-    for (col=1; col < width-1; col++) {
-      pix = image[row*width+col];
-      ip = code[row & 7][col & 1];
-      memset (sum, 0, sizeof sum);
-      for (g=8; g--; ) {
-    diff = pix[*ip++];
-    diff <<= *ip++;
-    sum[*ip++] += diff;
-      }
-      for (g=colors; --g; ) {
-    c = *ip++;
-    pix[c] = sum[c] / *ip++;
-      }
+    clip_max = cmdline.no_clip_color ? 0xffff : scaleMax;
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
+        for (col = 0; col < width; ++col) {
+            unsigned int c;
+            for (c = 0; c < colors; ++c) {
+                int val;
+                val = image[row*width+col][c];
+                if (val != 0) {
+                    val -= black;
+                    val *= pre_mul[c];
+                    image[row*width+col][c] = MAX(0, MIN(clip_max, val));
+                }
+            }
+        }
     }
-  }
-  if (cmdline.quick_interpolate)
-    return;
-
-  for (row=0; row < 8; row++) {     /* Precalculate for VNG */
-    for (col=0; col < 2; col++) {
-      ip = code[row][col];
-      for (cp=terms, t=0; t < 64; t++) {
-    y1 = *cp++;  x1 = *cp++;
-    y2 = *cp++;  x2 = *cp++;
-    weight = *cp++;
-    grads = *cp++;
-    color = FC(row+y1,col+x1);
-    if (FC(row+y2,col+x2) != color) continue;
-    diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1;
-    if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
-    *ip++ = (y1*width + x1)*4 + color;
-    *ip++ = (y2*width + x2)*4 + color;
-    *ip++ = weight;
-    for (g=0; g < 8; g++)
-      if (grads & 1<<g) *ip++ = g;
-    *ip++ = -1;
-      }
-      *ip++ = INT_MAX;
-      for (cp=chood, g=0; g < 8; g++) {
-    y = *cp++;  x = *cp++;
-    *ip++ = (y*width + x) * 4;
-    color = FC(row,col);
-    if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color)
-      *ip++ = (y*width + x) * 8 + color;
-    else
-      *ip++ = 0;
-      }
+}
+
+
+
+static void CLASS
+vngInterpolate(Image const image) {
+
+    /*
+      This algorithm is officially called "Interpolation using a
+      Threshold-based variable number of gradients," described in
+      http://www-ise.stanford.edu/~tingchen/algodep/vargra.html
+
+      I've extended the basic idea to work with non-Bayer filter arrays.
+      Gradients are numbered clockwise from NW=0 to W=7.
+    */
+
+    static const signed char *cp, terms[] = {
+  -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01,
+  -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01,
+  -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03,
+  -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06,
+  -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04,
+  -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01,
+  -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40,
+  -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11,
+  -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11,
+  -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22,
+  -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44,
+  -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10,
+  -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04,
+  +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40,
+  +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20,
+  +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08,
+  +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20,
+  +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44,
+  +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60,
+  +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80,
+  +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40,
+  +1,+0,+2,+1,0,(char)0x10
+    }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
+    unsigned short (*brow[5])[4], *pix;
+    int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4];
+    int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
+    int g, diff, thold, num, c;
+
+    for (row=0; row < 8; row++) {     /* Precalculate for bilinear */
+        for (col=1; col < 3; col++) {
+            ip = code[row][col & 1];
+            memset (sum, 0, sizeof sum);
+            for (y=-1; y <= 1; y++)
+                for (x=-1; x <= 1; x++) {
+                    shift = (y==0) + (x==0);
+                    if (shift == 2) continue;
+                    color = FC(row+y,col+x);
+                    *ip++ = (width*y + x)*4 + color;
+                    *ip++ = shift;
+                    *ip++ = color;
+                    sum[color] += 1 << shift;
+                }
+            FORC4
+                if (c != FC(row,col)) {
+                    *ip++ = c;
+                    *ip++ = sum[c];
+                }
+        }
     }
-  }
-  brow[4] = calloc (width*3, sizeof **brow);
-  merror (brow[4], "vng_interpolate()");
-  for (row=0; row < 3; row++)
-    brow[row] = brow[4] + row*width;
-  for (row=2; row < height-2; row++) {      /* Do VNG interpolation */
-    for (col=2; col < width-2; col++) {
-      pix = image[row*width+col];
-      ip = code[row & 7][col & 1];
-      memset (gval, 0, sizeof gval);
-      while ((g = ip[0]) != INT_MAX) {      /* Calculate gradients */
-    num = (diff = pix[g] - pix[ip[1]]) >> 31;
-    gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]);
-    ip += 5;
-    if ((g = ip[-1]) == -1) continue;
-    gval[g] += diff;
-    while ((g = *ip++) != -1)
-      gval[g] += diff;
-      }
-      ip++;
-      gmin = gmax = gval[0];            /* Choose a threshold */
-      for (g=1; g < 8; g++) {
-    if (gmin > gval[g]) gmin = gval[g];
-    if (gmax < gval[g]) gmax = gval[g];
-      }
-      if (gmax == 0) {
-    memcpy (brow[2][col], pix, sizeof *image);
-    continue;
-      }
-      thold = gmin + (gmax >> 1);
-      memset (sum, 0, sizeof sum);
-      color = FC(row,col);
-      for (num=g=0; g < 8; g++,ip+=2) {     /* Average the neighbors */
-    if (gval[g] <= thold) {
-      FORC4
-        if (c == color && ip[1])
-          sum[c] += (pix[c] + pix[ip[1]]) >> 1;
-        else
-          sum[c] += pix[ip[0] + c];
-      num++;
+    for (row=1; row < height-1; row++) {  /* Do bilinear interpolation */
+        for (col=1; col < width-1; col++) {
+            pix = image[row*width+col];
+            ip = code[row & 7][col & 1];
+            memset (sum, 0, sizeof sum);
+            for (g=8; g--; ) {
+                diff = pix[*ip++];
+                diff <<= *ip++;
+                sum[*ip++] += diff;
+            }
+            for (g=colors; --g; ) {
+                c = *ip++;
+                pix[c] = sum[c] / *ip++;
+            }
+        }
     }
-      }
-      FORC4 {                   /* Save to buffer */
-    t = pix[color];
-    if (c != color) {
-      t += (sum[c] - sum[color])/num;
-      if (t < 0) t = 0;
-      if (t > clip_max) t = clip_max;
+    if (cmdline.quick_interpolate)
+        return;
+
+    for (row=0; row < 8; row++) {     /* Precalculate for VNG */
+        for (col=0; col < 2; col++) {
+            ip = code[row][col];
+            for (cp=terms, t=0; t < 64; t++) {
+                y1 = *cp++;  x1 = *cp++;
+                y2 = *cp++;  x2 = *cp++;
+                weight = *cp++;
+                grads = *cp++;
+                color = FC(row+y1,col+x1);
+                if (FC(row+y2,col+x2) != color) continue;
+                diag =
+                    (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1;
+                if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
+                *ip++ = (y1*width + x1)*4 + color;
+                *ip++ = (y2*width + x2)*4 + color;
+                *ip++ = weight;
+                for (g=0; g < 8; g++)
+                    if (grads & 1<<g) *ip++ = g;
+                *ip++ = -1;
+            }
+            *ip++ = INT_MAX;
+            for (cp=chood, g=0; g < 8; g++) {
+                y = *cp++;  x = *cp++;
+                *ip++ = (y*width + x) * 4;
+                color = FC(row,col);
+                if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color)
+                    *ip++ = (y*width + x) * 8 + color;
+                else
+                    *ip++ = 0;
+            }
+        }
     }
-    brow[2][col][c] = t;
-      }
+    brow[4] = calloc (width*3, sizeof **brow);
+    merror (brow[4], "vngInterpolate()");
+    for (row=0; row < 3; row++)
+        brow[row] = brow[4] + row*width;
+    for (row=2; row < height-2; row++) {      /* Do VNG interpolation */
+        for (col=2; col < width-2; col++) {
+            pix = image[row*width+col];
+            ip = code[row & 7][col & 1];
+            memset (gval, 0, sizeof gval);
+            while ((g = ip[0]) != INT_MAX) {      /* Calculate gradients */
+                num = (diff = pix[g] - pix[ip[1]]) >> 31;
+                gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]);
+                ip += 5;
+                if ((g = ip[-1]) == -1) continue;
+                gval[g] += diff;
+                while ((g = *ip++) != -1)
+                    gval[g] += diff;
+            }
+            ip++;
+            gmin = gmax = gval[0];            /* Choose a threshold */
+            for (g=1; g < 8; g++) {
+                if (gmin > gval[g]) gmin = gval[g];
+                if (gmax < gval[g]) gmax = gval[g];
+            }
+            if (gmax == 0) {
+                memcpy (brow[2][col], pix, sizeof *image);
+                continue;
+            }
+            thold = gmin + (gmax >> 1);
+            memset (sum, 0, sizeof sum);
+            color = FC(row,col);
+            for (num=g=0; g < 8; g++,ip+=2) {     /* Average the neighbors */
+                if (gval[g] <= thold) {
+                    FORC4
+                        if (c == color && ip[1])
+                            sum[c] += (pix[c] + pix[ip[1]]) >> 1;
+                        else
+                            sum[c] += pix[ip[0] + c];
+                    num++;
+                }
+            }
+            FORC4 {                   /* Save to buffer */
+                t = pix[color];
+                if (c != color) {
+                    t += (sum[c] - sum[color])/num;
+                    if (t < 0) t = 0;
+                    if (t > clip_max) t = clip_max;
+                }
+                brow[2][col][c] = t;
+            }
+        }
+        if (row > 3)                /* Write buffer to image */
+            memcpy(image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+        for (g=0; g < 4; g++)
+            brow[(g-1) & 3] = brow[g];
     }
-    if (row > 3)                /* Write buffer to image */
-      memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
-    for (g=0; g < 4; g++)
-      brow[(g-1) & 3] = brow[g];
-  }
-  memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
-  memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
-  free (brow[4]);
+    memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+    memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
+    free (brow[4]);
 }
 
-#ifdef USE_LCMS
-static void 
-apply_profile(FILE *       const ifP,
-              const char * const pfname)
-{
-  char *prof;
-  cmsHPROFILE hInProfile=NULL, hOutProfile;
-  cmsHTRANSFORM hTransform;
-
-  if (pfname)
-    hInProfile = cmsOpenProfileFromFile (pfname, "r");
-  else if (profile_length) {
-    prof = malloc (profile_length);
-    merror (prof, "apply_profile()");
-    fseek (ifP, profile_offset, SEEK_SET);
-    fread (prof, 1, profile_length, ifP);
-    hInProfile = cmsOpenProfileFromMem (prof, profile_length);
-    free (prof);
-  }
-  if (!hInProfile) return;
-  if (cmdline.verbose)
-      pm_message( "Applying color profile...");
-  maximum = 0xffff;
-  use_gamma = use_coeff = 0;
-
-  hOutProfile = cmsCreate_sRGBProfile();
-  hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
-    hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
-  cmsDoTransform (hTransform, image, image, width*height);
-
-  cmsDeleteTransform (hTransform);
-  cmsCloseProfile (hInProfile);
-  cmsCloseProfile (hOutProfile);
-}
-#else
-static void 
-apply_profile(FILE *       const ifP,
-              const char * const pfname)
-{
-}
-#endif
 
-/*
+
+static void CLASS
+convertToRgb(Image        const image,
+             unsigned int const trim) {
+/*----------------------------------------------------------------------------
    Convert the entire image to RGB colorspace and build a histogram.
- */
-static void CLASS convert_to_rgb()
-{
-  int row, col, r, g, c=0;
-  unsigned short *img;
-  float rgb[3];
-
-  if (cmdline.document_mode)
-    colors = 1;
-  memset (histogram, 0, sizeof histogram);
-  for (row = trim; row < height-trim; row++)
-    for (col = trim; col < width-trim; col++) {
-      img = image[row*width+col];
-      if (cmdline.document_mode)
-    c = FC(row,col);
-      if (colors == 4 && !use_coeff)    /* Recombine the greens */
-    img[1] = (img[1] + img[3]) >> 1;
-      if (colors == 1)          /* RGB from grayscale */
-    for (r=0; r < 3; r++)
-      rgb[r] = img[c];
-      else if (use_coeff) {     /* RGB via coeff[][] */
-    for (r=0; r < 3; r++)
-      for (rgb[r]=g=0; g < colors; g++)
-        rgb[r] += img[g] * coeff[r][g];
-      } else                /* RGB from RGB (easy) */
-    goto norgb;
-      for (r=0; r < 3; r++) {
-    if (rgb[r] < 0)        rgb[r] = 0;
-    if (rgb[r] > clip_max) rgb[r] = clip_max;
-    img[r] = rgb[r];
-      }
-norgb:
-      for (r=0; r < 3; r++)
-    histogram[r][img[r] >> 3]++;
+
+   We modify 'image' to change it from whatever it is now to RGB.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    unsigned int c;
+    float rgb[3];  /* { red, green, blue } */
+
+    c = 0;  /* initial value */
+
+    if (cmdline.document_mode)
+        colors = 1;
+
+    memset(histogram, 0, sizeof histogram);
+
+    for (row = 0 + trim; row < height - trim; ++row) {
+        unsigned int col;
+        for (col = 0 + trim; col < width - trim; ++col) {
+            unsigned short * const img = image[row*width+col];
+
+            if (cmdline.document_mode)
+                c = FC(row,col);
+
+            if (colors == 4 && !use_coeff)
+                /* Recombine the greens */
+                img[1] = (img[1] + img[3]) / 2;
+
+            if (colors == 1) {
+                /* RGB from grayscale */
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    rgb[i] = img[c];
+            } else if (use_coeff) {
+                /* RGB via coeff[][] */
+                unsigned int i;
+                for (i = 0; i < 3; ++i) {
+                    unsigned int j;
+                    for (j = 0, rgb[i]= 0; j < colors; ++j)
+                        rgb[i] += img[j] * coeff[i][j];
+                }
+            } else {
+                /* RGB from RGB (easy) */
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    rgb[i] = img[i];
+            }
+            {
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    img[i] = MIN(clip_max, MAX(0, rgb[i]));
+            }
+            {
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    ++histogram[i][img[i] >> 3];
+            }
+        }
     }
 }
 
-static void CLASS fuji_rotate()
-{
-  int i, wide, high, row, col;
-  double step;
-  float r, c, fr, fc;
-  unsigned ur, uc;
-  unsigned short (*img)[4], (*pix)[4];
-
-  if (!fuji_width) return;
-  if (cmdline.verbose)
-    pm_message ("Rotating image 45 degrees...");
-  fuji_width = (fuji_width + shrink) >> shrink;
-  step = sqrt(0.5);
-  wide = fuji_width / step;
-  high = (height - fuji_width) / step;
-  img = calloc (wide*high, sizeof *img);
-  merror (img, "fuji_rotate()");
-
-  for (row=0; row < high; row++)
-    for (col=0; col < wide; col++) {
-      ur = r = fuji_width + (row-col)*step;
-      uc = c = (row+col)*step;
-      if (ur > height-2 || uc > width-2) continue;
-      fr = r - ur;
-      fc = c - uc;
-      pix = image + ur*width + uc;
-      for (i=0; i < colors; i++)
-    img[row*wide+col][i] =
-      (pix[    0][i]*(1-fc) + pix[      1][i]*fc) * (1-fr) +
-      (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
+
+
+static void CLASS
+fujiRotate(Image * const imageP) {
+
+    int wide;
+    int high;
+    unsigned int row;
+    double step;
+    float r, c, fr, fc;
+    unsigned short (*newImage)[4];
+    unsigned short (*pix)[4];
+
+    if (fuji_width > 0) {
+        if (cmdline.verbose)
+            pm_message ("Rotating image 45 degrees...");
+
+        fuji_width = (fuji_width + shrink) >> shrink;
+        step = sqrt(0.5);
+        wide = fuji_width / step;
+        high = (height - fuji_width) / step;
+        newImage = calloc (wide*high, sizeof *newImage);
+        merror (newImage, "fujiRotate()");
+
+        for (row = 0; row < high; ++row) {
+            unsigned int col;
+            for (col = 0; col < wide; ++col) {
+                unsigned int ur = r = fuji_width + (row-col)*step;
+                unsigned int uc = c = (row+col)*step;
+
+                unsigned int i;
+
+                if (ur > height-2 || uc > width-2)
+                    continue;
+
+                fr = r - ur;
+                fc = c - uc;
+                pix = (*imageP) + ur * width + uc;
+
+                for (i = 0; i < colors; ++i) {
+                    newImage[row*wide+col][i] =
+                        (pix[    0][i]*(1-fc) + pix[      1][i]*fc) * (1-fr) +
+                        (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
+                } 
+            }
+        }        
+        free(*imageP);
+        width  = wide;
+        height = high;
+        *imageP  = newImage;
+        fuji_width = 0;
     }
-  free (image);
-  width  = wide;
-  height = high;
-  image  = img;
-  fuji_width = 0;
 }
 
-static void CLASS flip_image()
-{
+
+
+static void CLASS
+flipImage(Image const image) {
+
     unsigned *flag;
-    int size, base, dest, next, row, col, temp;
+    int size, base, dest;
 
     struct imageCell {
         unsigned char contents[8];
     };
     struct imageCell * img;
-    struct imageCell hold;
 
     switch ((flip+3600) % 360) {
-    case 270:  flip = 5;  break;
-    case 180:  flip = 3;  break;
-    case  90:  flip = 6;
+    case 270:  flip = 0x5;  break;
+    case 180:  flip = 0x3;  break;
+    case  90:  flip = 0x6;
     }
     img = (struct imageCell *) image;
     size = height * width;
     flag = calloc ((size+31) >> 5, sizeof *flag);
-    merror (flag, "flip_image()");
-    for (base = 0; base < size; base++) {
-        if (flag[base >> 5] & (1 << (base & 31)))
-            continue;
-        dest = base;
-        hold = img[base];
-        while (1) {
-            if (flip & 4) {
-                row = dest % height;
-                col = dest / height;
-            } else {
-                row = dest / width;
-                col = dest % width;
+    merror (flag, "flipImage()");
+    for (base = 0; base < size; ++base) {
+        if (flag[base >> 5] & (1 << (base & 31))) {
+            /* nothing */
+        } else {
+            struct imageCell const hold = img[base];
+            dest = base;
+            while (true) {
+                unsigned int col;
+                unsigned int row;
+                int next;
+                if (flip & 0x4) {
+                    row = dest % height;
+                    col = dest / height;
+                } else {
+                    row = dest / width;
+                    col = dest % width;
+                }
+                if (flip & 0x2)
+                    row = height - 1 - row;
+                if (flip & 1)
+                    col = width - 1 - col;
+                next = row * width + col;
+                if (next == base)
+                    break;
+                flag[next >> 5] |= 1 << (next & 31);
+                img[dest] = img[next];
+                dest = next;
             }
-            if (flip & 2)
-                row = height - 1 - row;
-            if (flip & 1)
-                col = width - 1 - col;
-            next = row * width + col;
-            if (next == base) break;
-            flag[next >> 5] |= 1 << (next & 31);
-            img[dest] = img[next];
-            dest = next;
+            img[dest] = hold;
         }
-        img[dest] = hold;
     }
     free (flag);
-    if (flip & 4) {
-        temp = height;
+    if (flip & 0x4) {
+        int const oldHeight = height;
+        int const oldYmag = ymag;
+
         height = width;
-        width = temp;
-        temp = ymag;
+        width = oldHeight;
         ymag = xmag;
-        xmag = temp;
+        xmag = oldYmag;
     }
 }
 
-/*
-   Write the image as an RGB PAM image
- */
-static void CLASS write_pam_nonlinear (FILE *ofp)
-{
-  unsigned char lut[0x10000];
-  int perc, c, val, total, i, row, col;
-  float white=0, r;
-  struct pam pam;
-  tuple * tuplerow;
-
-  pam.size   = sizeof(pam);
-  pam.len    = PAM_STRUCT_SIZE(tuple_type);
-  pam.file   = ofp;
-  pam.width  = xmag*(width-trim*2);
-  pam.height = ymag*(height-trim*2);
-  pam.depth  = 3;
-  pam.format = PAM_FORMAT;
-  pam.maxval = 255;
-  strcpy(pam.tuple_type, "RGB");
-
-  pnm_writepaminit(&pam);
-
-  tuplerow = pnm_allocpamrow(&pam);
-
-  perc = width * height * 0.01;     /* 99th percentile white point */
-  if (fuji_width) perc /= 2;
-  FORC3 {
-    for (val=0x2000, total=0; --val > 32; )
-      if ((total += histogram[c][val]) > perc) break;
-    if (white < val) white = val;
-  }
-  white *= 8 / cmdline.bright;
-  for (i=0; i < 0x10000; i++) {
-    r = i / white;
-    val = 256 * ( !use_gamma ? r :
-    r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
-    if (val > 255) val = 255;
-    lut[i] = val;
-  }
-  for (row=trim; row < height-trim; row++) {
-      for (col=trim; col < width-trim; col++) {
-          unsigned int plane;
-          for (plane=0; plane < pam.depth; ++plane) {
-              unsigned int copy;
-              for (copy=0; copy < xmag; ++copy) {
-                  unsigned int const pamcol = xmag*(col-trim)+copy;
-                  tuplerow[pamcol][plane] = lut[image[row*width+col][plane]];
-              }
-          }
-      }
-      {
-          unsigned int copy;
-          for (copy=0; copy < ymag; ++copy)
-              pnm_writepamrow(&pam, tuplerow);
-      }
-  }
-  pnm_freepamrow(tuplerow);
+
+
+static void CLASS
+writePamLinear(FILE *       const ofP,
+               Image        const image,
+               unsigned int const trim) {
+/*----------------------------------------------------------------------------
+   Write the image 'image' to a 16-bit PAM file with linear color space
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    struct pam pam;
+    tuple * tuplerow;
+
+    pam.size   = sizeof(pam);
+    pam.len    = PAM_STRUCT_SIZE(tuple_type);
+    pam.file   = ofP;
+    pam.width  = width - trim - trim;
+    pam.height = height - trim - trim;
+    pam.depth  = 3;
+    pam.format = PAM_FORMAT;
+    pam.maxval = MAX(maximum, 256);
+    strcpy(pam.tuple_type, "RGB");
+
+    pnm_writepaminit(&pam);
+
+    tuplerow = pnm_allocpamrow(&pam);
+
+    for (row = 0 + trim; row < height - trim; ++row) {
+        unsigned int col;
+        for (col = 0 + trim; col < width - trim; ++col) {
+            unsigned int const pamCol = col - trim;
+            unsigned int plane;
+            for (plane = 0; plane < 3; ++plane)
+                tuplerow[pamCol][plane] = image[row*width+col][plane];
+        }
+        pnm_writepamrow(&pam, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
 }
 
-/*
-   Write the image to a 16-bit PAM file with linear color space
- */
-static void CLASS write_pam_linear (FILE *ofp)
-{
-  int row;
-
-  struct pam pam;
-  tuple * tuplerow;
-
-  if (maximum < 256) maximum = 256;
-
-  pam.size   = sizeof(pam);
-  pam.len    = PAM_STRUCT_SIZE(tuple_type);
-  pam.file   = ofp;
-  pam.width  = width-trim*2;
-  pam.height = height-trim*2;
-  pam.depth  = 3;
-  pam.format = PAM_FORMAT;
-  pam.maxval = MAX(maximum, 256);
-  strcpy(pam.tuple_type, "RGB");
-
-  pnm_writepaminit(&pam);
-
-  tuplerow = pnm_allocpamrow(&pam);
-
-  for (row = trim; row < height-trim; row++) {
-      unsigned int col;
-      for (col = trim; col < width-trim; col++) {
-          unsigned int const pamCol = col - trim;
-          unsigned int plane;
-          for (plane = 0; plane < 3; ++plane)
-              tuplerow[pamCol][plane] = image[row*width+col][plane];
-      }
-      pnm_writepamrow(&pam, tuplerow);
-  }
-  pnm_freepamrow(tuplerow);
+
+
+static void CLASS
+writePamNonlinear(FILE *       const ofP,
+                  Image        const image,
+                  unsigned int const trim) {
+/*----------------------------------------------------------------------------
+  Write the image 'image' as an RGB PAM image
+-----------------------------------------------------------------------------*/
+    unsigned char lut[0x10000];
+    int perc;
+    int c;
+    int total;
+    int i;
+    unsigned int row;
+    float white;
+    float r;
+    struct pam pam;
+    tuple * tuplerow;
+
+    white = 0;  /* initial value */
+
+    pam.size   = sizeof(pam);
+    pam.len    = PAM_STRUCT_SIZE(tuple_type);
+    pam.file   = ofP;
+    pam.width  = xmag*(width-trim*2);
+    pam.height = ymag*(height-trim*2);
+    pam.depth  = 3;
+    pam.format = PAM_FORMAT;
+    pam.maxval = 255;
+    strcpy(pam.tuple_type, "RGB");
+
+    pnm_writepaminit(&pam);
+
+    tuplerow = pnm_allocpamrow(&pam);
+
+    perc = width * height * 0.01;     /* 99th percentile white point */
+    if (fuji_width) perc /= 2;
+    FORC3 {
+        int val;
+        for (val=0x2000, total=0; --val > 32; )
+            if ((total += histogram[c][val]) > perc) break;
+        if (white < val)
+            white = val;
+    }
+    white *= 8 / cmdline.bright;
+    for (i=0; i < 0x10000; ++i) {
+        int val;
+        r = i / white;
+        val = 256 * ( !use_gamma ? r :
+                      r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
+        lut[i] = MIN(255, val);
+    }
+
+    for (row = 0 + trim; row < height - trim; ++row) {
+        unsigned int col;
+        for (col = 0 + trim; col < width - trim; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < pam.depth; ++plane) {
+                sample const value = lut[image[row*width+col][plane]];
+                unsigned int copy;
+                for (copy = 0; copy < xmag; ++copy) {
+                    unsigned int const pamcol = xmag*(col-trim)+copy;
+                    tuplerow[pamcol][plane] = value;
+                }
+            }
+        }
+        {
+            unsigned int copy;
+            for (copy = 0; copy < ymag; ++copy)
+                pnm_writepamrow(&pam, tuplerow);
+        }
+    }
+    pnm_freepamrow(tuplerow);
 }
 
 
 
 static void CLASS
-writePam(FILE * const ofP,
-         bool   const linear) {
+writePam(FILE *       const ofP,
+         Image        const image,
+         bool         const linear,
+         unsigned int const trim) {
 
     if (linear)
-        write_pam_linear(ofP);
+        writePamLinear(ofP, image, trim);
     else
-        write_pam_nonlinear(ofP);
+        writePamNonlinear(ofP, image, trim);
 }
 
 
 
-
 static void CLASS
-convertIt(FILE *    const ifP,
-          FILE *    const ofP,
-          loadRawFn const load_raw) {
+convertIt(FILE *       const ifP,
+          FILE *       const ofP,
+          LoadRawFn *  const load_raw) {
+
+    Image image;
+    unsigned int trim;
 
     shrink = cmdline.half_size && filters;
     iheight = (height + shrink) >> shrink;
     iwidth  = (width  + shrink) >> shrink;
-    image = calloc (iheight*iwidth*sizeof *image + meta_length, 1);
+    image = calloc (iheight*iwidth*sizeof(*image) + meta_length, 1);
     merror (image, "main()");
     meta_data = (char *) (image + iheight*iwidth);
     if (cmdline.verbose)
@@ -809,43 +887,49 @@ convertIt(FILE *    const ifP,
 
     ifp = ifP;  /* Set global variable for (*load_raw)() */
 
-    load_raw();
-    bad_pixels();
+    load_raw(image);
+    fixBadPixels(image);
     height = iheight;
     width  = iwidth;
     if (is_foveon) {
         if (cmdline.verbose)
             pm_message ("Foveon interpolation...");
-        foveon_interpolate(coeff);
+        foveon_interpolate(image, coeff);
     } else {
-        scale_colors();
+        scaleColors(image);
     }
-    if (shrink) filters = 0;
-    trim = 0;
+    if (shrink)
+        filters = 0;
+
     if (filters && !cmdline.document_mode) {
         trim = 1;
         if (cmdline.verbose)
             pm_message ("%s interpolation...",
                         cmdline.quick_interpolate ? "Bilinear":"VNG");
-        vng_interpolate();
-    }
-    fuji_rotate();
-    apply_profile(ifP, cmdline.profile);
+        vngInterpolate(image);
+    } else
+        trim = 0;
+
+    fujiRotate(&image);
+
     if (cmdline.verbose)
         pm_message ("Converting to RGB colorspace...");
-    convert_to_rgb();
+    convertToRgb(image, trim);
 
     if (flip) {
         if (cmdline.verbose)
             pm_message ("Flipping image %c:%c:%c...",
                         flip & 1 ? 'H':'0', flip & 2 ? 'V':'0', 
                         flip & 4 ? 'T':'0');
-        flip_image();
+        flipImage(image);
     }
-    writePam(ofP, cmdline.linear);
+    writePam(ofP, image, cmdline.linear, trim);
+
+    free(image);
 }
 
 
+
 int 
 main (int argc, char **argv) {
 
@@ -853,7 +937,7 @@ main (int argc, char **argv) {
 
     FILE * ifP;
     int rc;
-    loadRawFn load_raw;
+    LoadRawFn * load_raw;
 
     pnm_init(&argc, argv);
 
@@ -863,8 +947,6 @@ main (int argc, char **argv) {
 
     ifP = pm_openr(cmdline.inputFileName);
 
-    image = NULL;
-    
     rc = identify(ifP,
                   cmdline.use_secondary, cmdline.use_camera_rgb,
                   cmdline.red_scale, cmdline.blue_scale,
@@ -883,7 +965,6 @@ main (int argc, char **argv) {
     }
     pm_close(ifP);
     pm_close(ofP);
-    free(image);
 
     return 0;
 }
diff --git a/converter/other/cameratopam/cameratopam.h b/converter/other/cameratopam/cameratopam.h
new file mode 100644
index 00000000..9ff33cb3
--- /dev/null
+++ b/converter/other/cameratopam/cameratopam.h
@@ -0,0 +1,6 @@
+#ifndef CAMERATOPAM_H_INCLUDED
+#define CAMERATOPAM_H_INCLUDED
+
+typedef unsigned short (*Image)[4];
+
+#endif
diff --git a/converter/other/cameratopam/canon.c b/converter/other/cameratopam/canon.c
index a34771d0..96a6210b 100644
--- a/converter/other/cameratopam/canon.c
+++ b/converter/other/cameratopam/canon.c
@@ -9,7 +9,7 @@
 
 
 void 
-canon_600_load_raw(void) {
+canon_600_load_raw(Image const image) {
     unsigned char  data[1120], *dp;
     unsigned short pixel[896], *pix;
     int irow, orow, col;
@@ -42,7 +42,7 @@ canon_600_load_raw(void) {
 
 
 void
-canon_a5_load_raw(void) {
+canon_a5_load_raw(Image const image) {
     unsigned char  data[1940], *dp;
     unsigned short pixel[1552], *pix;
     int row, col;
@@ -97,7 +97,7 @@ canon_has_lowbits()
 
 
 void 
-canon_compressed_load_raw(void) {
+canon_compressed_load_raw(Image const image) {
     unsigned short *pixel, *prow;
     int lowbits, i, row, r, col, save, val;
     unsigned irow, icol;
diff --git a/converter/other/cameratopam/canon.h b/converter/other/cameratopam/canon.h
index 041cdc4d..fe6a5a08 100644
--- a/converter/other/cameratopam/canon.h
+++ b/converter/other/cameratopam/canon.h
@@ -1,13 +1,12 @@
 #ifndef CANON_H_INCLUDED
 #define CANON_H_INCLUDED
 
-void 
-canon_600_load_raw(void);
+#include "camera.h"
 
-void
-canon_a5_load_raw(void);
+LoadRawFn canon_600_load_raw;
 
-void 
-canon_compressed_load_raw(void);
+LoadRawFn canon_a5_load_raw;
+
+LoadRawFn canon_compressed_load_raw;
 
 #endif
diff --git a/converter/other/cameratopam/dng.c b/converter/other/cameratopam/dng.c
index 44d45b02..bddfd9f4 100644
--- a/converter/other/cameratopam/dng.c
+++ b/converter/other/cameratopam/dng.c
@@ -4,70 +4,105 @@
 #include "dng.h"
 
 void 
-dng_coeff (double cc[4][4], double cm[4][3], double xyz[3])
-{
-  static const double rgb_xyz[3][3] = {     /* RGB from XYZ */
-    {  3.240479, -1.537150, -0.498535 },
-    { -0.969256,  1.875992,  0.041556 },
-    {  0.055648, -0.204043,  1.057311 } };
+dng_coeff (double cc[4][4],
+           double cm[4][3],
+           double xyz[3]) {
+    static const double rgb_xyz[3][3] = {     /* RGB from XYZ */
+        {  3.240479, -1.537150, -0.498535 },
+        { -0.969256,  1.875992,  0.041556 },
+        {  0.055648, -0.204043,  1.057311 } };
 #if 0
-  static const double xyz_rgb[3][3] = {     /* XYZ from RGB */
-    { 0.412453, 0.357580, 0.180423 },
-    { 0.212671, 0.715160, 0.072169 },
-    { 0.019334, 0.119193, 0.950227 } };
+    static const double xyz_rgb[3][3] = {     /* XYZ from RGB */
+        { 0.412453, 0.357580, 0.180423 },
+        { 0.212671, 0.715160, 0.072169 },
+        { 0.019334, 0.119193, 0.950227 } };
 #endif
-  double cam_xyz[4][3], xyz_cam[3][4], invert[3][6], num;
-  int i, j, k;
+    double cam_xyz[4][3], xyz_cam[3][4], invert[3][6];
+    unsigned int i;
 
-  memset (cam_xyz, 0, sizeof cam_xyz);
-  for (i=0; i < colors; i++)
-    for (j=0; j < 3; j++)
-      for (k=0; k < colors; k++)
-    cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j];
+    for (i = 0; i < colors; ++i) {
+        unsigned int j;
+        for (j = 0; j < 3; ++j) {
+            unsigned int k;
+            for (k = 0, cam_xyz[i][j] = 0.0; k < colors; ++k) {
+                cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j];
+            }
+        }
+    }
+    for (i = 0; i < colors; ++i) {
+        unsigned int j;
+        double camXyzSum;
+
+        for (j = 0, camXyzSum = 0.0; j < 3; ++j)
+            camXyzSum += cam_xyz[i][j];
 
-  for (i=0; i < colors; i++) {
-    for (num=j=0; j < 3; j++)
-      num += cam_xyz[i][j];
-    for (j=0; j < 3; j++)
-      cam_xyz[i][j] /= num;
-    pre_mul[i] = 1 / num;
-  }
-  for (i=0; i < 3; i++) {
-    for (j=0; j < 6; j++)
-      invert[i][j] = j == i+3;
-    for (j=0; j < 3; j++)
-      for (k=0; k < colors; k++)
-    invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j];
-  }
-  for (i=0; i < 3; i++) {
-    num = invert[i][i];
-    for (j=0; j < 6; j++)       /* Normalize row i */
-      invert[i][j] /= num;
-    for (k=0; k < 3; k++) {     /* Subtract it from other rows */
-      if (k==i) continue;
-      num = invert[k][i];
-      for (j=0; j < 6; j++)
-    invert[k][j] -= invert[i][j] * num;
+        for (j = 0; j < 3; ++j)
+            cam_xyz[i][j] /= camXyzSum;
+
+        pre_mul[i] = 1 / camXyzSum;
+    }
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < 6; ++j)
+            invert[i][j] = j == i+3;
+        for (j = 0; j < 3; ++j) {
+            unsigned int k;
+            for (k = 0; k < colors; ++k)
+                invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j];
+        }
     }
-  }
-  memset (xyz_cam, 0, sizeof xyz_cam);
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (k=0; k < 3; k++)
-    xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k];
+    for (i = 0; i < 3; ++i) {
+        double const num = invert[i][i];
+        unsigned int j;
+        unsigned int k;
+        for (j = 0; j < 6; ++j)       /* Normalize row i */
+            invert[i][j] /= num;
+        for (k = 0; k < 3; ++k) {     /* Subtract it from other rows */
+            if (k != i) {
+                double const num = invert[k][i];
+                unsigned int j;
+                for (j = 0; j < 6; ++j)
+                    invert[k][j] -= invert[i][j] * num;
+            }
+        }
+    }
+
+    memset(xyz_cam, 0, sizeof xyz_cam);
 
-  memset (coeff, 0, sizeof coeff);
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (k=0; k < 3; k++)
-    coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j];
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < colors; ++j) {
+            unsigned int k;
+            for (k = 0; k < 3; ++k)
+                xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k];
+        }
+    }
+    memset (coeff, 0, sizeof coeff);
 
-  for (num=j=0; j < colors; j++)
-    num += coeff[1][j];
-  for (i=0; i < 3; i++) {
-    for (j=0; j < colors; j++)
-      coeff[i][j] /= num;
-  }
-  use_coeff = 1;
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < colors; ++j) {
+            unsigned int k;
+            for (k = 0; k < 3; ++k)
+                coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j];
+        }
+    }
+    {
+        double greenSum;
+        unsigned int j;
+        unsigned int i;
+
+        for (j = 0, greenSum = 0.0; j < colors; ++j)
+            greenSum += coeff[1][j];
+
+        for (i = 0; i < 3; ++i) {
+            unsigned int j;
+            for (j = 0; j < colors; ++j)
+                coeff[i][j] /= greenSum;
+        }
+    }
+    use_coeff = 1;
 }
 
+
+
diff --git a/converter/other/cameratopam/dng.h b/converter/other/cameratopam/dng.h
index 01e7e0a5..56293563 100644
--- a/converter/other/cameratopam/dng.h
+++ b/converter/other/cameratopam/dng.h
@@ -1,2 +1,4 @@
 void 
-dng_coeff (double cc[4][4], double cm[4][3], double xyz[3]);
+dng_coeff(double cc[4][4],
+          double cm[4][3],
+          double xyz[3]);
diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c
index f6d074fb..a3e5449a 100644
--- a/converter/other/cameratopam/foveon.c
+++ b/converter/other/cameratopam/foveon.c
@@ -211,7 +211,7 @@ foveon_load_camf() {
 
 
 void  
-foveon_load_raw() {
+foveon_load_raw(Image const image) {
 
     struct decode *dindex;
     short diff[1024], pred[3];
@@ -391,7 +391,8 @@ static int  foveon_apply_curve (short *curve, int i)
 }
 
 void  
-foveon_interpolate(float coeff[3][4]) {
+foveon_interpolate(Image const image,
+                   float coeff[3][4]) {
 
     static const short hood[] = { 
         -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 };
@@ -472,8 +473,8 @@ foveon_interpolate(float coeff[3][4]) {
     sgrow = calloc (dim[1], sizeof *sgrow);
     sgx = (width + dim[1]-2) / (dim[1]-1);
 
-    black = calloc (height, sizeof *black);
-    for (row=0; row < height; row++) {
+    black = calloc (height, sizeof(black[0]));
+    for (row=0; row < height; ++row) {
         unsigned int i;
         for (i=0; i < 3; ++i) {
             unsigned int j;
@@ -486,8 +487,8 @@ foveon_interpolate(float coeff[3][4]) {
               foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3
               - ddft[0][c][0] ) / 4 - ddft[0][c][1];
     }
-    memcpy (black, black+8, sizeof (*black)*8);
-    memcpy (black+height-11, black+height-22, 11*sizeof *black);
+    memcpy (black, black+8, 8 * sizeof(black[0]));
+    memcpy (black+height-11, black+height-22, 11*(sizeof black[0]));
     memcpy (last, black, sizeof last);
 
     for (row=1; row < height-1; row++) {
diff --git a/converter/other/cameratopam/foveon.h b/converter/other/cameratopam/foveon.h
index f3177e50..c9bf48a8 100644
--- a/converter/other/cameratopam/foveon.h
+++ b/converter/other/cameratopam/foveon.h
@@ -1,13 +1,16 @@
 #include "pm.h"
 
+#include "cameratopam.h"
+#include "camera.h"
+
 void 
 parse_foveon(FILE * const ifp);
 
 void  
-foveon_interpolate(float coeff[3][4]);
+foveon_interpolate(Image const image,
+                   float coeff[3][4]);
 
-void 
-foveon_load_raw(void);
+LoadRawFn foveon_load_raw;
 
 void  
 foveon_coeff(int * const useCoeffP,
diff --git a/converter/other/cameratopam/global_variables.h b/converter/other/cameratopam/global_variables.h
index c8732d5a..2bfc08c9 100644
--- a/converter/other/cameratopam/global_variables.h
+++ b/converter/other/cameratopam/global_variables.h
@@ -23,7 +23,6 @@ extern time_t timestamp;
 extern int is_foveon;
 extern int is_dng;
 extern int is_canon;
-extern unsigned short (*image)[4];
 extern int maximum;
 extern int clip_max;
 extern short order;
diff --git a/converter/other/cameratopam/identify.c b/converter/other/cameratopam/identify.c
index f0b21ce5..394ba0a7 100644
--- a/converter/other/cameratopam/identify.c
+++ b/converter/other/cameratopam/identify.c
@@ -2,6 +2,8 @@
 #include <string.h>
 
 #include "pm.h"
+#include "pm_c_util.h"
+#include "nstring.h"
 
 #include "global_variables.h"
 #include "util.h"
@@ -21,7 +23,6 @@ static bool const have64BitArithmetic = false;
 #endif
 
 
-static loadRawFn load_raw;
 
 /* This does the same as the function of the same name in the GNU C library */
 static const char *memmem_internal (const char *haystack, size_t haystacklen,
@@ -34,16 +35,20 @@ static const char *memmem_internal (const char *haystack, size_t haystacklen,
     return NULL;
 }
 
-/*
-  Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
-*/
+
+
 static void 
-adobe_coeff()
-{
-    static const struct {
-        const char *prefix;
+adobeCoeff(const char * const make,
+           const char * const model) {
+    /*
+      Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
+    */
+    struct CoeffTableEntry {
+        const char * prefix;
         short trans[12];
-    } table[] = {
+    }; 
+
+    static struct CoeffTableEntry const table[] = {
         { "Canon EOS D2000C",
           { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
         { "Canon EOS D30",
@@ -229,26 +234,33 @@ adobe_coeff()
     double cm[4][3];
     double xyz[] = { 1,1,1 };
     char name[130];
-    int i, j;
+    unsigned int i;
 
-    for (i=0; i < 4; i++)
-        for (j=0; j < 4; j++)
-            cc[i][j] = i == j;
+    /* Make an identity matrix (1's on the diagonal) */
+    for (i = 0; i < 4; ++i) {
+        unsigned int j;
+        for (j = 0; j < 4; ++j)
+            cc[i][j] = (i == j);
+    }
     sprintf (name, "%s %s", make, model);
-    for (i=0; i < sizeof table / sizeof *table; i++)
-        if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) {
+
+    for (i = 0; i < ARRAY_SIZE(table); ++i) {
+        const struct CoeffTableEntry * const entryP = &table[i];
+
+        if (strneq(name, entryP->prefix, strlen(entryP->prefix))) {
             unsigned int j;
-            for (j=0; j < 12; j++)
-                cm[j/4][j%4] = table[i].trans[j];
-            dng_coeff (cc, cm, xyz);
+            for (j = 0; j < 12; ++j)
+                cm[j/4][j%4] = entryP->trans[j];
+            dng_coeff(cc, cm, xyz);
+
             break;
         }
+    }
 }
 
-/*
-  Identify which camera created this file, and set global variables
-  accordingly.  Return nonzero if the file cannot be decoded.
-*/
+
+
+
 int
 identify(FILE *       const ifp,
          bool         const use_secondary,
@@ -257,8 +269,11 @@ identify(FILE *       const ifp,
          float        const blue_scale,
          unsigned int const four_color_rgb,
          const char * const inputFileName,
-         loadRawFn *  const loadRawFnP)
-{
+         LoadRawFn ** const loadRawFnP) {
+/*----------------------------------------------------------------------------
+  Identify which camera created this file, and set global variables
+  accordingly.  Return nonzero if the file cannot be decoded.
+-----------------------------------------------------------------------------*/
     char head[32];
     char * c;
     unsigned hlen, fsize, i;
@@ -292,6 +307,7 @@ identify(FILE *       const ifp,
         { "Canon", "NIKON", "EPSON", "Kodak", "OLYMPUS", "PENTAX",
           "MINOLTA", "Minolta", "Konica", "CASIO" };
     float tmp;
+    LoadRawFn * load_raw;
 
     /*  What format is this file?  Set make[] if we recognize it. */
 
@@ -310,9 +326,6 @@ identify(FILE *       const ifp,
     colors = 3;
     for (i=0; i < 0x1000; i++) curve[i] = i;
     maximum = 0xfff;
-#ifdef USE_LCMS
-    profile_length = 0;
-#endif
 
     order = get2(ifp);
     hlen = get4(ifp);
@@ -412,6 +425,9 @@ identify(FILE *       const ifp,
         memmove (model, model+i, 64-i);
     make[63] = model[63] = model2[63] = 0;
 
+    if (verbose)
+        fprintf(stderr, "Make = '%s', Model = '%s'\n", make, model);
+
     if (make[0] == 0) {
         pm_message ("unrecognized file format.");
         return 1;
@@ -442,7 +458,7 @@ identify(FILE *       const ifp,
         goto dng_skip;
     }
 
-/*  We'll try to decode anything from Canon or Nikon. */
+    /*  We'll try to decode anything from Canon or Nikon. */
 
     if (!filters) filters = 0x94949494;
     if ((is_canon = !strcmp(make,"Canon")))
@@ -452,7 +468,7 @@ identify(FILE *       const ifp,
         load_raw = nikon_is_compressed() ?
             nikon_compressed_load_raw : nikon_load_raw;
 
-/* Set parameters based on camera name (for non-DNG files). */
+    /* Set parameters based on camera name (for non-DNG files). */
 
     if (is_foveon) {
         if (!have64BitArithmetic)
@@ -1145,7 +1161,8 @@ identify(FILE *       const ifp,
             flip = 2;
         }
     }
-    if (!use_coeff) adobe_coeff();
+    if (!use_coeff)
+        adobeCoeff(make, model);
 dng_skip:
     if (!load_raw || !height) {
         pm_message ("This program cannot handle data from %s %s.",
diff --git a/converter/other/cameratopam/identify.h b/converter/other/cameratopam/identify.h
index 012b807c..62c9aae5 100644
--- a/converter/other/cameratopam/identify.h
+++ b/converter/other/cameratopam/identify.h
@@ -1,4 +1,4 @@
-typedef void (*loadRawFn)();
+#include "camera.h"
 
 int
 identify(FILE *       const ifp,
@@ -8,4 +8,4 @@ identify(FILE *       const ifp,
          float        const blue_scale,
          unsigned int const four_color_rgb,
          const char * const inputFileName,
-         loadRawFn *  const loadRawFnP);
+         LoadRawFn ** const loadRawFnP);
diff --git a/converter/other/cameratopam/ljpeg.c b/converter/other/cameratopam/ljpeg.c
index 18423f4f..29e4ff98 100644
--- a/converter/other/cameratopam/ljpeg.c
+++ b/converter/other/cameratopam/ljpeg.c
@@ -87,9 +87,10 @@ ljpeg_row (struct jhead *jh)
     }
 }
 
+
 void  
-lossless_jpeg_load_raw(void)
-{
+lossless_jpeg_load_raw(Image const image) {
+
   int jwide, jrow, jcol, val, jidx, i, row, col;
   struct jhead jh;
   int min=INT_MAX;
diff --git a/converter/other/cameratopam/ljpeg.h b/converter/other/cameratopam/ljpeg.h
index 60832a3d..cfd68eb4 100644
--- a/converter/other/cameratopam/ljpeg.h
+++ b/converter/other/cameratopam/ljpeg.h
@@ -1,11 +1,12 @@
+#include "camera.h"
+
 struct jhead {
   int bits, high, wide, clrs, vpred[4];
   struct decode *huff[4];
   unsigned short *row;
 };
 
-void  
-lossless_jpeg_load_raw(void);
+LoadRawFn lossless_jpeg_load_raw;
 
 int  
 ljpeg_start (FILE * ifp, struct jhead *jh);
diff --git a/converter/other/fiasco/config.h b/converter/other/fiasco/config.h
index 64b905f8..57b3518d 100644
--- a/converter/other/fiasco/config.h
+++ b/converter/other/fiasco/config.h
@@ -56,9 +56,6 @@
 /* Define if you have the strcasecmp function.  */
 #define HAVE_STRCASECMP 1
 
-/* Define if you have the strdup function.  */
-#define HAVE_STRDUP 1
-
 /* Define if you have the <X11/extensions/XShm.h> header file.  */
 /* #undef HAVE_X11_EXTENSIONS_XSHM_H */
 
diff --git a/converter/other/fiasco/lib/misc.c b/converter/other/fiasco/lib/misc.c
index 11e2da54..782ed1e9 100644
--- a/converter/other/fiasco/lib/misc.c
+++ b/converter/other/fiasco/lib/misc.c
@@ -396,22 +396,6 @@ memmove (void *v_dst, const void *v_src, size_t n)
 }
 #endif /* not HAVE_MEMMOVE */
 
-#ifndef HAVE_STRDUP
-char *
-strdup (const char *s)
-/*
- *  Duplicate given string 's'.
- *
- *  Return value:
- *	pointer to new string value
- */
-{
-   assert (s);
-   
-   return strcpy (Calloc (strlen (s) + 1, sizeof (char)), s);
-}
-#endif /* not HAVE_STRDUP */
-
 /* Note that some systems have a "log2()" in the math library and some
    have a "log2" macro.  So we name ours Log2.  But to avoid lots of
    differences from the original fiasco source code, we define a
diff --git a/converter/other/fiasco/lib/misc.h b/converter/other/fiasco/lib/misc.h
index 29456590..28fd8b5a 100644
--- a/converter/other/fiasco/lib/misc.h
+++ b/converter/other/fiasco/lib/misc.h
@@ -72,10 +72,6 @@ memmove(void *dest, const void *src, size_t n);
 
 double
 Log2 (double x);
-#ifndef HAVE_STRDUP
-char *
-strdup (const char *s);
-#endif
 #ifndef HAVE_STRCASECMP
 bool_t
 strcaseeq (const char *s1, const char *s2);
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index 7d517aad..9a543532 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -19,6 +19,7 @@
    describe the Lempel-Ziv base.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* for strcaseeq */
 #include <string.h>
 #include <assert.h>
diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c
index eec4495a..d6bd6ef5 100644
--- a/converter/other/ipdb.c
+++ b/converter/other/ipdb.c
@@ -19,6 +19,7 @@
  *   Authors:  Eric A. Howe (mu@trends.net)
  *             Bryan Henderson, 2010
  */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Ensure strdup() is in <string.h> */
 #include <assert.h>
 #include <time.h>
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
index 858c0fa4..405de9c9 100644
--- a/converter/other/jpeg2000/jpeg2ktopam.c
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -9,8 +9,13 @@
 *****************************************************************************/
 
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-/* Make sure strdup() is in string.h and int_fast32_t is in inttypes.h */
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
+    /* In 2014.09, this was _XOPEN_SOURCE 600, with a comment saying it was
+       necessary to make <inttypes.h> define int_fast32_t, etc. on AIX.
+       <jasper/jasper.h> does use int_fast32_t and does include <inttypes.h>,
+       but plenty of source files of libjasper do to0, and they did not have
+       _XOPEN_SOURCE 600, so it would seem to be superfluous here too.
+    */
 #include <string.h>
 
 #include <jasper/jasper.h>
diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
index 349018e1..b8905518 100644
--- a/converter/other/jpeg2000/pamtojpeg2k.c
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -9,8 +9,14 @@
 *****************************************************************************/
 
 #define _BSD_SOURCE 1    /* Make sure strdup() is in string.h */
-/* Make sure strdup() is in string.h and int_fast32_t is in inttypes.h */
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
+    /* In 2014.09, this was _XOPEN_SOURCE 600, with a comment saying it was
+       necessary to make <inttypes.h> define int_fast32_t, etc. on AIX.
+       <jasper/jasper.h> does use int_fast32_t and does include <inttypes.h>,
+       but plenty of source files of libjasper do too, and they did not have
+       _XOPEN_SOURCE 600, so it would seem to be superfluous here too.
+    */
+
 #include <string.h>
 
 #include <jasper/jasper.h>
diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c
index 9bb662b7..f043d721 100644
--- a/converter/other/pamtopnm.c
+++ b/converter/other/pamtopnm.c
@@ -17,23 +17,23 @@
 #include "shhopt.h"
 #include "mallocvar.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input files */
-    unsigned int assume;    /* -assume option */
+    const char * inputFileName;  /* Name of input file */
+    unsigned int assume;
 };
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
+    optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -49,16 +49,18 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
 }
 
 
@@ -105,19 +107,19 @@ validateTupleType(struct pam const inpam,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
-    FILE* ifP;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
     int eof;   /* no more images in input stream */
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PNM image */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     eof = FALSE;
     while (!eof) {
diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c
index b556e0d2..f8a69424 100644
--- a/converter/other/pgmtoppm.c
+++ b/converter/other/pgmtoppm.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <string.h>
 
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
index 554dc8f3..b253442f 100644
--- a/converter/other/pstopnm.c
+++ b/converter/other/pstopnm.c
@@ -116,7 +116,7 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "nocrop",     OPT_FLAG,  NULL, &cmdlineP->nocrop,         0);
     OPTENT3(0, "pbm",        OPT_FLAG,  NULL, &pbmOpt ,                  0);
     OPTENT3(0, "pgm",        OPT_FLAG,  NULL, &pgmOpt,                   0);
-    OPTENT3(0, "ppm",        OPT_FLAG,  NULL, &ppmOpt,                  0);
+    OPTENT3(0, "ppm",        OPT_FLAG,  NULL, &ppmOpt,                   0);
     OPTENT3(0, "verbose",    OPT_FLAG,  NULL, &cmdlineP->verbose,        0);
     OPTENT3(0, "xborder",    OPT_FLOAT, &cmdlineP->xborder, NULL,        0);
     OPTENT3(0, "xmax",       OPT_UINT,  &cmdlineP->xmax, &xmaxSpec,      0);
@@ -125,8 +125,8 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "ymax",       OPT_UINT,  &cmdlineP->ymax, &ymaxSpec,      0);
     OPTENT3(0, "ysize",      OPT_UINT,  &cmdlineP->ysize, &ysizeSpec,    0);
     OPTENT3(0, "dpi",        OPT_UINT,  &cmdlineP->dpi, &dpiSpec,        0);
-    OPTENT3(0, "portrait",   OPT_FLAG,  NULL, &portraitOpt,             0);
-    OPTENT3(0, "landscape",  OPT_FLAG,  NULL, &landscapeOpt,            0);
+    OPTENT3(0, "portrait",   OPT_FLAG,  NULL, &portraitOpt,              0);
+    OPTENT3(0, "landscape",  OPT_FLAG,  NULL, &landscapeOpt,             0);
     OPTENT3(0, "stdout",     OPT_FLAG,  NULL, &cmdlineP->stdoutSpec,     0);
     OPTENT3(0, "textalphabits", OPT_UINT,
             &cmdlineP->textalphabits,  &textalphabitsSpec, 0);
diff --git a/converter/other/svgtopam.c b/converter/other/svgtopam.c
index 26530b9b..137f4732 100644
--- a/converter/other/svgtopam.c
+++ b/converter/other/svgtopam.c
@@ -26,6 +26,7 @@
    
 ============================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #define _POSIX_SOURCE   /* Make sure fileno() is in <stdio.h> */
 #include <assert.h>
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
index 214c02aa..595d4937 100644
--- a/converter/other/tifftopnm.c
+++ b/converter/other/tifftopnm.c
@@ -78,7 +78,7 @@
 #define PHOTOMETRIC_DEPTH 32768
 #endif
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -96,7 +96,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -219,6 +219,8 @@ tiffToImageDim(unsigned int   const tiffCols,
     }
 }
 
+
+
 static void
 getTiffDimensions(TIFF *         const tiffP,
                   unsigned int * const colsP,
@@ -338,15 +340,15 @@ readDirectory(TIFF *               const tiffP,
 
 
 static void
-readscanline(TIFF *         const tif, 
-             unsigned char  scanbuf[], 
-             int            const row, 
-             int            const plane,
-             unsigned int   const cols, 
-             unsigned short const bps,
-             unsigned short const spp,
-             unsigned short const fillorder,
-             unsigned int * const samplebuf) {
+readscanline(TIFF *          const tif, 
+             unsigned char * const scanbuf,
+             int             const row, 
+             int             const plane,
+             unsigned int    const cols, 
+             unsigned short  const bps,
+             unsigned short  const spp,
+             unsigned short  const fillorder,
+             unsigned int *  const samplebuf) {
 /*----------------------------------------------------------------------------
    Read one scanline out of the Tiff input and store it into samplebuf[].
    Unlike the scanline returned by the Tiff library function, samplebuf[]
@@ -440,8 +442,11 @@ readscanline(TIFF *         const tif,
 
 
 static void
-pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
-                xelval * const r_p, xelval * const b_p, xelval * const g_p) {
+pick_cmyk_pixel(unsigned int const samplebuf[],
+                int          const sampleCursor,
+                xelval *     const redP,
+                xelval *     const bluP,
+                xelval *     const grnP) {
 
     /* Note that the TIFF spec does not say which of the 4 samples is
        which, but common sense says they're in order C,M,Y,K.  Before
@@ -449,10 +454,10 @@ pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
        But combined with a compensating error in the CMYK->RGB
        calculation, it had the same effect as C,M,Y,K.
     */
-    unsigned int const c = samplebuf[sample_cursor+0];
-    unsigned int const m = samplebuf[sample_cursor+1];
-    unsigned int const y = samplebuf[sample_cursor+2];
-    unsigned int const k = samplebuf[sample_cursor+3];
+    unsigned int const c = samplebuf[sampleCursor + 0];
+    unsigned int const m = samplebuf[sampleCursor + 1];
+    unsigned int const y = samplebuf[sampleCursor + 2];
+    unsigned int const k = samplebuf[sampleCursor + 3];
 
     /* The CMYK->RGB formula used by TIFFRGBAImageGet() in the TIFF 
        library is the following, (with some apparent confusion with
@@ -470,9 +475,9 @@ pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
        Yellow ink removes blue light from what the white paper reflects.  
     */
 
-    *r_p = 255 - MIN(255, c + k);
-    *g_p = 255 - MIN(255, m + k);
-    *b_p = 255 - MIN(255, y + k);
+    *redP = 255 - MIN(255, c + k);
+    *grnP = 255 - MIN(255, m + k);
+    *bluP = 255 - MIN(255, y + k);
 }
 
 
@@ -509,9 +514,9 @@ analyzeImageType(TIFF *             const tif,
                  unsigned short     const photomet,
                  xelval *           const maxvalP, 
                  int *              const formatP, 
-                 xel                      colormap[],
+                 xel *              const colormap,
                  bool               const headerdump,
-                 struct cmdlineInfo const cmdline) {
+                 struct CmdlineInfo const cmdline) {
 
     bool grayscale; 
 
@@ -1078,8 +1083,8 @@ pnmOut_writeRow(pnmOut *     const pnmOutP,
 
 static void
 convertRow(unsigned int   const samplebuf[], 
-           xel                  xelrow[], 
-           gray                 alpharow[], 
+           xel *          const xelrow, 
+           gray *         const alpharow,
            int            const cols, 
            xelval         const maxval, 
            unsigned short const photomet, 
@@ -1155,9 +1160,9 @@ convertRow(unsigned int   const samplebuf[],
 
 
 static void
-scale32to16(unsigned int       samplebuf[],
-            unsigned int const cols,
-            unsigned int const spp) {
+scale32to16(unsigned int * const samplebuf,
+            unsigned int   const cols,
+            unsigned int   const spp) {
 /*----------------------------------------------------------------------------
   Convert every sample in samplebuf[] to something that can be expressed
   in 16 bits, assuming it takes 32 bits now.
@@ -1170,9 +1175,9 @@ scale32to16(unsigned int       samplebuf[],
 
 
 static void
-convertMultiPlaneRow(TIFF *         const tif,
-                     xel                   xelrow[],
-                     gray                  alpharow[],
+convertMultiPlaneRow(TIFF *          const tif,
+                     xel *           const xelrow,
+                     gray *          const alpharow,
                      int             const cols,
                      xelval          const maxval,
                      int             const row,
@@ -1188,15 +1193,15 @@ convertMultiPlaneRow(TIFF *         const tif,
        for the blues.
     */
 
-    int col;
-
     if (photomet != PHOTOMETRIC_RGB)
         pm_error("This is a multiple-plane file, but is not an RGB "
                  "file.  This program does not know how to handle that.");
     else {
+        unsigned int col;
+
         /* First, clear the buffer so we can add red, green,
            and blue one at a time.  
-                */
+        */
         for (col = 0; col < cols; ++col) 
             PPM_ASSIGN(xelrow[col], 0, 0, 0);
 
@@ -1249,8 +1254,8 @@ convertRasterByRows(pnmOut *       const pnmOutP,
                     bool           const verbose) {
 /*----------------------------------------------------------------------------
    With the TIFF header all processed (and relevant information from it in 
-   our arguments), write out the TIFF raster to the file images *imageoutFile
-   and *alphaFile.
+   our arguments), write out the TIFF raster to the Netpbm output files
+   as described by *pnmOutP.
 
    Do this one row at a time, employing the TIFF library's
    TIFFReadScanline.
@@ -1270,12 +1275,12 @@ convertRasterByRows(pnmOut *       const pnmOutP,
            row we are presently converting.
         */
 
-    int row;
+    unsigned int row;
 
     if (verbose)
         pm_message("Converting row by row ...");
 
-    scanbuf = (unsigned char *) malloc(TIFFScanlineSize(tif));
+    MALLOCARRAY(scanbuf, TIFFScanlineSize(tif));
     if (scanbuf == NULL)
         pm_error("can't allocate memory for scanline buffer");
 
@@ -1409,20 +1414,23 @@ convertTiffRaster(uint32 *        const raster,
 
 
 
-enum convertDisp {CONV_DONE, CONV_OOM, CONV_UNABLE, CONV_FAILED, 
+enum convertDisp {CONV_DONE,
+                  CONV_OOM,
+                  CONV_UNABLE,
+                  CONV_FAILED, 
                   CONV_NOTATTEMPTED};
 
 static void
-convertRasterInMemory(pnmOut *       const pnmOutP,
-                      xelval         const maxval,
-                      TIFF *         const tif,
-                      unsigned short const photomet, 
-                      unsigned short const planarconfig,
-                      unsigned short const bps,
-                      unsigned short const spp,
-                      unsigned short const fillorder,
-                      xel            const colormap[],
-                      bool           const verbose,
+convertRasterInMemory(pnmOut *           const pnmOutP,
+                      xelval             const maxval,
+                      TIFF *             const tif,
+                      unsigned short     const photomet, 
+                      unsigned short     const planarconfig,
+                      unsigned short     const bps,
+                      unsigned short     const spp,
+                      unsigned short     const fillorder,
+                      xel                const colormap[],
+                      bool               const verbose,
                       enum convertDisp * const statusP) {
 /*----------------------------------------------------------------------------
    With the TIFF header all processed (and relevant information from
@@ -1512,6 +1520,7 @@ convertRaster(pnmOut *           const pnmOutP,
               bool               const verbose) {
 
     enum convertDisp status;
+
     if (byrow || !flipOk)
         status = CONV_NOTATTEMPTED;
     else {
@@ -1551,7 +1560,7 @@ static void
 convertImage(TIFF *             const tifP,
              FILE *             const alphaFileP,
              FILE *             const imageoutFileP,
-             struct cmdlineInfo const cmdline) {
+             struct CmdlineInfo const cmdline) {
 
     struct tiffDirInfo tiffDir;
     int format;
@@ -1588,7 +1597,7 @@ static void
 convertIt(TIFF *             const tifP,
           FILE *             const alphaFile, 
           FILE *             const imageoutFile,
-          struct cmdlineInfo const cmdline) {
+          struct CmdlineInfo const cmdline) {
 
     unsigned int imageSeq;
     bool eof;
@@ -1613,7 +1622,7 @@ convertIt(TIFF *             const tifP,
 int
 main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     TIFF * tif;
     FILE * alphaFile;
     FILE * imageoutFile;
@@ -1654,6 +1663,7 @@ main(int argc, const char * argv[]) {
     pm_strfree(cmdline.inputFilename);
 
     /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.  */
+       code, via various function calls.
+    */
     return 0;
 }
diff --git a/converter/pbm/pbmtonokia.c b/converter/pbm/pbmtonokia.c
index 839e6cc1..bf3b9e41 100644
--- a/converter/pbm/pbmtonokia.c
+++ b/converter/pbm/pbmtonokia.c
@@ -4,6 +4,7 @@
    Copyright information is at end of file.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 #include <string.h>
 #include <assert.h>
diff --git a/converter/ppm/ppmtompeg/gethostname.c b/converter/ppm/ppmtompeg/gethostname.c
index 014b42e8..d20af17c 100644
--- a/converter/ppm/ppmtompeg/gethostname.c
+++ b/converter/ppm/ppmtompeg/gethostname.c
@@ -1,3 +1,4 @@
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include <string.h>
diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c
index 86ce7c7a..5cd7b099 100644
--- a/converter/ppm/ppmtompeg/mpeg.c
+++ b/converter/ppm/ppmtompeg/mpeg.c
@@ -30,6 +30,7 @@
  * HEADER FILES *
  *==============*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include "all.h"
diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c
index b8ef37f1..bc788552 100644
--- a/converter/ppm/ppmtompeg/ppmtompeg.c
+++ b/converter/ppm/ppmtompeg/ppmtompeg.c
@@ -30,6 +30,7 @@
  * HEADER FILES *
  *==============*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include <assert.h>
diff --git a/converter/ppm/ppmtopict.c b/converter/ppm/ppmtopict.c
index 7e19d755..36464b6c 100644
--- a/converter/ppm/ppmtopict.c
+++ b/converter/ppm/ppmtopict.c
@@ -193,32 +193,42 @@ putRect(FILE * const ifP,
 #define     runtochar(c)    (257-(c))
 #define     counttochar(c)  ((c)-1)
 
-static int
-putRow(FILE * const ifP,
-       unsigned int const row,
-       unsigned int const cols,
-       pixel *      const rowpixels,
-       char *       const packed) {
-
+static void
+putRow(FILE *         const ofP,
+       unsigned int   const row,
+       unsigned int   const cols,
+       pixel *        const rowpixels,
+       char *         const outBuf,
+       unsigned int * const outCountP) {
+/*----------------------------------------------------------------------------
+   Write the row rowpixels[], which is 'cols' pixels wide and is row 'row' of
+   the image, to file *ofP in PICT format.
+
+   Return as *outCountP the number of bytes we write to *ofP.
+
+   Use buffer 'outBuf'.
+-----------------------------------------------------------------------------*/
     unsigned int i;
     unsigned int count;
     unsigned int run;
     unsigned int rep;
-    unsigned int oc;
-    pixel * pP;
-    pixel lastp;
+    unsigned int outCount;
+    pixel lastpix;
     char * p;
 
-    run = count = 0;
-    for (i = 0, pP = rowpixels + cols-1, p = packed, lastp = *pP;
-         i < cols;
-         ++i, lastp = *pP, --pP) {
+    run = 0;
+    count = 0;
+    lastpix = rowpixels[cols-1];
 
-        if (PPM_EQUAL(lastp, *pP))
+    for (i = 0, p = &outBuf[0]; i < cols; ++i) {
+
+        pixel const pix = rowpixels[cols - 1 - i];
+
+        if (PPM_EQUAL(lastpix, pix))
             ++run;
         else if (run < RUN_THRESH) {
             while (run > 0) {
-                *p++ = ppm_lookupcolor(cht, &lastp);
+                *p++ = ppm_lookupcolor(cht, &lastpix);
                 --run;
                 ++count;
                 if (count == MAX_COUNT) {
@@ -233,17 +243,18 @@ putRow(FILE * const ifP,
             count = 0;
             while (run > 0) {
                 rep = MIN(run, MAX_RUN);
-                *p++ = ppm_lookupcolor(cht, &lastp);
+                *p++ = ppm_lookupcolor(cht, &lastpix);
                 *p++ = runtochar(rep);
                 assert(run >= rep);
                 run -= rep;
             }
             run = 1;
         }
+        lastpix = pix;
     }
     if (run < RUN_THRESH) {
         while (run > 0) {
-            *p++ = ppm_lookupcolor(cht, &lastp);
+            *p++ = ppm_lookupcolor(cht, &lastpix);
             --run;
             ++count;
             if (count == MAX_COUNT) {
@@ -257,7 +268,7 @@ putRow(FILE * const ifP,
         count = 0;
         while (run > 0) {
             rep = MIN(run, MAX_RUN);
-            *p++ = ppm_lookupcolor(cht, &lastp);
+            *p++ = ppm_lookupcolor(cht, &lastpix);
             *p++ = runtochar(rep);
             assert(run >= rep);
             run -= rep;
@@ -268,22 +279,22 @@ putRow(FILE * const ifP,
         *p++ = counttochar(count);
 
     {
-        unsigned int const packcols = p - packed;
+        unsigned int const packcols = p - outBuf;
             /* How many we wrote */
         if (cols-1 > 200) {
-            putShort(ifP, packcols);
-            oc = packcols + 2;
+            putShort(ofP, packcols);
+            outCount = packcols + 2;
         } else {
-            putc(packcols, ifP);
-            oc = packcols + 1;
+            putc(packcols, ofP);
+            outCount = packcols + 1;
         }
     }
     /* now write out the packed row */
-    while(p != packed) {
+    while (p != outBuf) {
         --p;
-        putc(*p, ifP);
+        putc(*p, ofP);
     }
-    return oc;
+    *outCountP = outCount;
 }
 
 
@@ -291,46 +302,44 @@ putRow(FILE * const ifP,
 # if 0
 
 /* real dumb putRow with no compression */
-static unsigned int
-putRow(FILE *       const ifP,
-       unsigned int const row,
-       unsigned int const cols,
-       pixel *      const rowpixels,
-       char *       const packed) {
+static void
+putRow(FILE *         const ifP,
+       unsigned int   const row,
+       unsigned int   const cols,
+       pixel *        const rowpixels,
+       char *         const outBuf,
+       unsigned int * const outCountP) {
 
     unsigned int const bc = cols + (cols + MAX_COUNT - 1) / MAX_COUNT;
 
     unsigned int i;
-    unsigned int oc;
-    pixel * pP;
 
     if (bc > 200) {
         putShort(ifP, bc);
-        oc = bc + 2;
+        *outCountP = bc + 2;
     }  else {
         putc(bc, ifP);
-        oc = bc + 1;
+        *outCountP = bc + 1;
     }
-    for (i = 0, pP = rowpixels; i < cols;) {
-        if (cols - i > MAX_COUNT) {
+    for (col = 0; col < cols;) {
+        if (cols - col > MAX_COUNT) {
             unsigned int j;
             putc(MAX_COUNT - 1, ifP);
             for (j = 0; j < MAX_COUNT; ++j) {
-                putc(ppm_lookupcolor(cht, pP), ifP);
-                ++pP;
+                putc(ppm_lookupcolor(cht, &rowPixels[col]), ifP);
+                ++col
             }
-            i += MAX_COUNT;
+            col += MAX_COUNT;
         } else {
             unsigned int j;
-            putc(cols - i - 1, ifP);
-            for (j = 0; j < cols - i; ++j) {
-                putc(ppm_lookupcolor(cht, pP), ifP);
+            putc(cols - col - 1, ifP);
+            for (j = 0; j < cols - col; ++j) {
+                putc(ppm_lookupcolor(cht, &rowPixels[col]), ifP);
                 ++pP;
             }
-            i = cols;
+            col = cols;
         }
     }
-    return oc;
 }
 #endif  /* 0 */
 
@@ -346,7 +355,7 @@ main(int argc, const char ** argv) {
     int rows, cols;
     unsigned int row;
     pixel ** pixels;
-    char * packed;
+    char * outBuf;
     pixval maxval;
     long lmaxval, rval, gval, bval;
     colorhist_vector chv;
@@ -441,10 +450,12 @@ main(int argc, const char ** argv) {
     putShort(stdout, 0);            /* mode */
 
     /* Finally, write out the data. */
-    packed = malloc((unsigned)(cols+cols/MAX_COUNT+1));
-    for (row = 0, oc = 0; row < rows; row++)
-        oc += putRow(stdout, row, cols, pixels[row], packed);
-
+    outBuf = malloc((unsigned)(cols+cols/MAX_COUNT+1));
+    for (row = 0, oc = 0; row < rows; ++row) {
+        unsigned int rowSize;
+        putRow(stdout, row, cols, pixels[row], outBuf, &rowSize);
+        oc += rowSize;
+    }
     /* if we wrote an odd number of pixdata bytes, pad */
     if (oc & 0x1)
         putc(0, stdout);
@@ -457,3 +468,5 @@ main(int argc, const char ** argv) {
     return 0;
 }
 
+
+
diff --git a/doc/HISTORY b/doc/HISTORY
index 1e663c8b..fbb549fe 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,29 +4,49 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-14.09.26 BJH  Release 10.67.06
+14.09.26 BJH  Release 10.68.00
 
-              cameratopam: fix incorrect output introduced in Netpbm 10.51
-              (June 2010).
+              pnmconvol: add -bias .
 
-14.09.08 BJH  Release 10.67.05
+              Remove pnmcomp, install a pnmcomp symlink for pamcomp.
+
+              Fix incorrect option parsing when there are multiple common
+              options (e.g. -plain -quiet).  Always broken.  (Possibility of
+              specifying multiple common options that don't cause the program
+              to ignore every other option (like -version) was new in Netpbm
+              10.10 (October 2002).
 
               cameratopam: fix buffer overflow.  Always present.  (cameratopam
               was new in Netpbm 10.28 (June 2005)).
-              
-              Build: fix universal build failure introduced in 10.67.04.
 
-14.09.04 BJH  Release 10.67.04
+              cameratopam: fix incorrect output introduced in Netpbm 10.51
+              (June 2010).
+
+              ppmtopict: Fix unconditional crash.  Introduced in 
+              Netpbm 10.52 (September 2010).
 
               pcdovtoppm: Fix crash due to invalid operator == on some
               systems.  Always broken (pcdovtoppm was new sometime between
               Netpbm 9.25 (March 2002) and Netpbm 10.11 (Februrary 2010)).
 
-              Build: fix compile failure due to use of reserved word
-              'stdout'.
-
-              Build with 'installosf': Fix crash due to invalid operator ==
-              on some systems.
+              Build: change _XOPEN_SOURCE from 600 back to 500 in 7 files.  It
+              was changed from 500 to 600 in Subversion revision 1731 in
+              Netpbm 10.60 (September 2012) because that made a similar
+              version of Netpbm compile on Mac OSX.  Without it, strdup did
+              not get defined.  But this is apparently a bug in Mac OSX, since
+              X/Open 500 does have strdup.  Furthermore, many other Netpbm
+              files use strdup and apparently compile OK on Mac OSX without
+              600.  Finally, we see today that Illumos system header files
+              deliberately kill the compilation if the compiler is pre-C99 and
+              _XOPEN_SOURCE is 600.  So we go back to 500 and if the problem
+              on Mac OSX gets reported again, we will look more deeply.
+
+              Build: change _XOPEN_SOURCE from 600 back to 500 in
+              jpeg2ktopam.c and pamtojpeg2k.c.  It was changed from 500 to 600
+              in Netpbm 10.41 (December 2007), reportedly to get int_fast32_t,
+              etc. defined on AIX, but other files that use int_fast32_t
+              don't have it today, so that must be wrong.  See above for the
+              drawback of 600.
 
               Build: fix undefined symbols in fiasco converters with
               static libraries.
@@ -34,7 +54,8 @@ CHANGE HISTORY
               Build: provide means of setting the default search path for
               rgb.txt color database via config.mk.
 
-14.08.24 BJH  Release 10.67.03
+              Build: fix bug which prevents JBIG converters from building with
+              internal JBIG library.  Introduced in 10.67.00.
 
               Build: fix bug which makes build of Ppmsvga fail (which is
               attempted only on a system with libvga).  Introduced in Netpbm
@@ -47,21 +68,14 @@ CHANGE HISTORY
               Build: fix build failure on SCO OpenServer due to SIGURG not
               existing.  Broken in Netpbm 10.49 (December 2009).
 
-14.08.12 BJH  Release 10.67.02
-
-              Build: fix bug which prevents JBIG converters from building with
-              internal JBIG library.  Introduced in 10.67.00.
+              Build: Declare _XOPEN_SOURCE >= 500 in source files that use
+              strdup.
 
-14.08.11 BJH  Release 10.67.01
-
-              Fix incorrect option parsing when there are multiple common
-              options (e.g. -plain -quiet).  Always broken.  (Possibility of
-              specifying multiple common options that don't cause the program
-              to ignore every other option (like -version) was new in Netpbm
-              10.10 (October 2002).
+              Build: fix compile failure due to use of reserved word
+              'stdout'.
 
-              ppmtopict: Fix unconditional crash.  Introduced in Netpbm 10.52
-              (September 2010).
+              Build with 'installosf': Fix crash due to invalid operator ==
+              on some systems.
 
 14.06.29 BJH  Release 10.67.00
 
diff --git a/doc/INSTALL b/doc/INSTALL
index 9ed6e131..f933722d 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -81,6 +81,34 @@ The test facility was new in Netpbm 10.61 (December 2012).
 For further information on the tests read the document TESTS.
 
 
+OVERRIDING INTERNAL LIBRARIES
+-----------------------------
+
+There are a few esoteric libraries that are distributed separately from Netpbm
+but of which Netpbm contains a copy too, for convenience.  The build normally
+uses the internal Netpbm copy, but you can configure the build to use a
+version of the library that is installed on your system.
+
+For the JBIG library, set the JBIGLIB and JBIGHDR_DIR variables in
+config.mk to the appropriate values.
+
+For the Utah Raster Toolkit (aka URT aka RLE) library, set the URTLIB and
+URTHDR_DIR vairables in config.mk to the appropriate values.
+
+The appropriate value for the xxxLIB variable is the full file name of the
+link library (e.g. "/usr/local/lib/jbigkit/libjbig.so") or just the file name
+part of it (e.g. "libjbig.so") if the library is in your linker's default
+search path.
+
+The appropriate value for the xxxHDR_DIR variable is the full directory name
+of the directory that contains the interface header files for the library
+identified by the xxxLIB variable (E.g. "usr/local/include/jbigkit") or a null
+string if those header files are in your compiler's default search path.
+
+If you use the 'configure' program, be sure to edit config.mk _after_ you
+run 'configure', since 'configure' generates config.mk.
+
+
 AUTOMATING THE BUILD
 --------------------
 
diff --git a/doc/USERDOC b/doc/USERDOC
index fa0ac574..f067059a 100644
--- a/doc/USERDOC
+++ b/doc/USERDOC
@@ -125,7 +125,7 @@ Also, these methods require manual effort, and technical
 understanding, on your part to set up.  Setting it up is too complex
 for an automated process to do it for you with any significant
 integrity.  The examples are guidelines and you shouldn't expect them
-to work literally in your situtation.
+to work literally in your situation.
 
 Here is an old example of making troff pages, which doesn't actually
 work anymore, but might be helpful.  You'll probably want to use a Subversion
diff --git a/editor/Makefile b/editor/Makefile
index 398c4aea..c163f220 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -26,7 +26,7 @@ PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
 	       pbmclean pbmmask pbmpscale pbmreduce \
 	       pgmdeshadow pgmenhance \
 	       pgmmedian \
-	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop \
+	       pnmalias pnmcat pnmconvol pnmcrop \
 	       pnmgamma \
 	       pnmhisteq pnminvert pnmmontage \
 	       pnmnlfilt pnmnorm pnmpad pnmpaste \
@@ -94,3 +94,7 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	rm -f ppmquantall$(EXE) ; \
 	$(SYMLINK) pnmquantall ppmquantall
+# In August 2014, pamcomp replaced pnmcomp
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmcomp$(EXE) ; \
+	$(SYMLINK) pamcomp$(EXE) pnmcomp$(EXE)
diff --git a/editor/pamperspective.c b/editor/pamperspective.c
index a75e5243..16715c2e 100644
--- a/editor/pamperspective.c
+++ b/editor/pamperspective.c
@@ -18,6 +18,7 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is int string.h */
 
 #include <assert.h>
diff --git a/editor/pamundice.c b/editor/pamundice.c
index 93ce69f1..9a80e46d 100644
--- a/editor/pamundice.c
+++ b/editor/pamundice.c
@@ -491,7 +491,12 @@ openInStreams(struct pam         inpam[],
 /*----------------------------------------------------------------------------
    Open the input files for a single horizontal slice (there's one file
    for each vertical slice) and read the Netpbm headers from them.  Return
-   the pam structures to describe each.
+   the pam structures to describe each as inpam[].
+
+   Open the files for horizontal slice number 'rank', assuming there are
+   'fileCount' vertical slices (so open 'fileCount' files).  Use
+   inputFilePattern[] with each rank and file number to compute the name of
+   each file.
 -----------------------------------------------------------------------------*/
     unsigned int file;
 
@@ -507,7 +512,9 @@ openInStreams(struct pam         inpam[],
 static void
 closeInFiles(struct pam         pam[],
              unsigned int const fileCount) {
-
+/*----------------------------------------------------------------------------
+   Close the 'fileCount' input file streams represented by pam[].
+-----------------------------------------------------------------------------*/
     unsigned int file;
     
     for (file = 0; file < fileCount; ++file)
diff --git a/editor/pnmcomp.c b/editor/pnmcomp.c
deleted file mode 100644
index b782d69d..00000000
--- a/editor/pnmcomp.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/* +-------------------------------------------------------------------+ */
-/* | Copyright 1992, David Koblas.                                     | */
-/* |   Permission to use, copy, modify, and distribute this software   | */
-/* |   and its documentation for any purpose and without fee is hereby | */
-/* |   granted, provided that the above copyright notice appear in all | */
-/* |   copies and that both that copyright notice and this permission  | */
-/* |   notice appear in supporting documentation.  This software is    | */
-/* |   provided "as is" without express or implied warranty.           | */
-/* +-------------------------------------------------------------------+ */
-
-/*
-
-    DON'T ADD NEW FUNCTION TO THIS PROGRAM.  ADD IT TO pamcomp.c INSTEAD.
-
-*/
-
-
-
-#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
-#include <string.h>
-
-#include "pm_c_util.h"
-#include "pnm.h"
-#include "shhopt.h"
-#include "mallocvar.h"
-
-enum horizPos {BEYONDLEFT, LEFT, CENTER, RIGHT, BEYONDRIGHT};
-enum vertPos {ABOVE, TOP, MIDDLE, BOTTOM, BELOW};
-
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *underlyingFilespec;  /* '-' if stdin */
-    const char *overlayFilespec;
-    const char *alphaFilespec;
-    const char *outputFilespec;  /* '-' if stdout */
-    int xoff, yoff;   /* value of xoff, yoff options */
-    float opacity;
-    unsigned int alphaInvert;
-    enum horizPos align;
-    enum vertPos valign;
-};
-
-
-
-
-static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
-/*----------------------------------------------------------------------------
-   parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
-
-   If command line is internally inconsistent (invalid options, etc.),
-   issue error message to stderr and abort program.
-
-   Note that the strings we return are stored in the storage that
-   was passed to us as the argv array.  We also trash *argv.
------------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to pm_optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    char *align, *valign;
-    unsigned int xoffSpec, yoffSpec, alignSpec, valignSpec, opacitySpec,
-        alphaSpec;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "invert",     OPT_FLAG,   NULL,                  
-            &cmdlineP->alphaInvert,       0 );
-    OPTENT3(0, "xoff",       OPT_INT,    &cmdlineP->xoff,       
-            &xoffSpec,       0 );
-    OPTENT3(0, "yoff",       OPT_INT,    &cmdlineP->yoff,       
-            &yoffSpec,       0 );
-    OPTENT3(0, "opacity",    OPT_FLOAT, &cmdlineP->opacity,
-            &opacitySpec,       0 );
-    OPTENT3(0, "alpha",      OPT_STRING, &cmdlineP->alphaFilespec,
-            &alphaSpec,  0 );
-    OPTENT3(0, "align",      OPT_STRING, &align,
-            &alignSpec,  0 );
-    OPTENT3(0, "valign",     OPT_STRING, &valign,
-            &valignSpec,  0 );
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-
-    if (!xoffSpec)
-        cmdlineP->xoff = 0;
-    if (!yoffSpec)
-        cmdlineP->yoff = 0;
-    if (!alphaSpec)
-        cmdlineP->alphaFilespec = NULL;
-
-    if (alignSpec) {
-        if (strcasecmp(align, "BEYONDLEFT") == 0)
-            cmdlineP->align = BEYONDLEFT;
-        else if (strcasecmp(align, "LEFT") == 0)
-            cmdlineP->align = LEFT;
-        else if (strcasecmp(align, "CENTER") == 0)
-            cmdlineP->align = CENTER;
-        else if (strcasecmp(align, "RIGHT") == 0)
-            cmdlineP->align = RIGHT;
-        else if (strcasecmp(align, "BEYONDRIGHT") == 0)
-            cmdlineP->align = BEYONDRIGHT;
-        else
-            pm_error("Invalid value for align option: '%s'.  Only LEFT, "
-                     "RIGHT, CENTER, BEYONDLEFT, and BEYONDRIGHT are valid.", 
-                     align);
-    } else 
-        cmdlineP->align = LEFT;
-
-    if (valignSpec) {
-        if (strcasecmp(valign, "ABOVE") == 0)
-            cmdlineP->valign = ABOVE;
-        else if (strcasecmp(valign, "TOP") == 0)
-            cmdlineP->valign = TOP;
-        else if (strcasecmp(valign, "MIDDLE") == 0)
-            cmdlineP->valign = MIDDLE;
-        else if (strcasecmp(valign, "BOTTOM") == 0)
-            cmdlineP->valign = BOTTOM;
-        else if (strcasecmp(valign, "BELOW") == 0)
-            cmdlineP->valign = BELOW;
-        else
-            pm_error("Invalid value for valign option: '%s'.  Only TOP, "
-                     "BOTTOM, MIDDLE, ABOVE, and BELOW are valid.", 
-                     align);
-    } else 
-        cmdlineP->valign = TOP;
-
-    if (!opacitySpec) 
-        cmdlineP->opacity = 1.0;
-
-    if (argc-1 < 1)
-        pm_error("Need at least one argument: file specification of the "
-                 "overlay image.");
-
-    cmdlineP->overlayFilespec = argv[1];
-
-    if (argc-1 >= 2)
-        cmdlineP->underlyingFilespec = argv[2];
-    else
-        cmdlineP->underlyingFilespec = "-";
-
-    if (argc-1 >= 3)
-        cmdlineP->outputFilespec = argv[3];
-    else
-        cmdlineP->outputFilespec = "-";
-
-    if (argc-1 > 3)
-        pm_error("Too many arguments.  Only acceptable arguments are: "
-                 "overlay image, underlying image, output image");
-}
-
-
-
-
-static void
-warnOutOfFrame( int const originLeft,
-                int const originTop, 
-                int const overCols,
-                int const overRows,
-                int const underCols,
-                int const underRows ) {
-    if (originLeft >= underCols)
-        pm_message("WARNING: the overlay is entirely off the right edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The horizontal "
-                   "overlay position you selected is %d, "
-                   "and the underlying image "
-                   "is only %d pixels wide.", originLeft, underCols );
-    else if (originLeft + overCols <= 0)
-        pm_message("WARNING: the overlay is entirely off the left edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The horizontal "
-                   "overlay position you selected is %d and the overlay is "
-                   "only %d pixels wide.", originLeft, overCols);
-    else if (originTop >= underRows)
-        pm_message("WARNING: the overlay is entirely off the bottom edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The vertical "
-                   "overlay position you selected is %d, "
-                   "and the underlying image "
-                   "is only %d pixels high.", originTop, underRows );
-    else if (originTop + overRows <= 0)
-        pm_message("WARNING: the overlay is entirely off the top edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The vertical "
-                   "overlay position you selected is %d and the overlay is "
-                   "only %d pixels high.", originTop, overRows);
-}
-
-
-
-static void
-computeOverlayPosition(const int underCols, const int underRows,
-                       const int overCols, const int overRows,
-                       const struct cmdlineInfo cmdline, 
-                       int * const originLeftP,
-                       int * const originTopP) {
-/*----------------------------------------------------------------------------
-   Determine where to overlay the overlay image, based on the options the
-   user specified and the realities of the image dimensions.
-
-   The origin may be outside the underlying image (so e.g. *originLeftP may
-   be negative or > image width).  That means not all of the overlay image
-   actually gets used.  In fact, there may be no overlap at all.
------------------------------------------------------------------------------*/
-    int xalign, yalign;
-
-    switch (cmdline.align) {
-    case BEYONDLEFT:  xalign = -overCols;              break;
-    case LEFT:        xalign = 0;                      break;
-    case CENTER:      xalign = (underCols-overCols)/2; break;
-    case RIGHT:       xalign = underCols - overCols;   break;
-    case BEYONDRIGHT: xalign = underCols;              break;
-    }
-    switch (cmdline.valign) {
-    case ABOVE:       yalign = -overRows;              break;
-    case TOP:         yalign = 0;                      break;
-    case MIDDLE:      yalign = (underRows-overRows)/2; break;
-    case BOTTOM:      yalign = underRows - overRows;   break;
-    case BELOW:       yalign = underRows;              break;
-    }
-    *originLeftP = xalign + cmdline.xoff;
-    *originTopP  = yalign + cmdline.yoff;
-
-    warnOutOfFrame( *originLeftP, *originTopP, 
-                    overCols, overRows, underCols, underRows );    
-}
-
-
-
-static pixval
-composeComponents(pixval const compA, 
-                  pixval const compB,
-                  float  const distrib,
-                  pixval const maxval) {
-/*----------------------------------------------------------------------------
-  Compose a single component of each of two pixels, with 'distrib' being
-  the fraction of 'compA' in the result, 1-distrib the fraction of 'compB'.
-  
-  Both inputs are based on a maxval of 'maxval', and so is our result.
-  
-  Note that while 'distrib' in the straightforward case is always in
-  [0,1], it can in fact be negative or greater than 1.  We clip the
-  result as required to return a legal pixval.
------------------------------------------------------------------------------*/
-    return MIN(maxval, MAX(0, (int)compA * distrib +
-                              (int)compB * (1.0 - distrib) + 
-                              0.5
-                          )
-              );
-}
-
-
-
-static pixel
-composePixels(pixel  const pixelA,
-              pixel  const pixelB,
-              float  const distrib,
-              pixval const maxval) {
-/*----------------------------------------------------------------------------
-  Compose two pixels 'pixelA' and 'pixelB', with 'distrib' being the
-  fraction of 'pixelA' in the result, 1-distrib the fraction of 'pixelB'.
-
-  Both inputs are based on a maxval of 'maxval', and so is our result.
-  
-  Note that while 'distrib' in the straightforward case is always in
-  [0,1], it can in fact be negative or greater than 1.  We clip the
-  result as required to return a legal pixval.
------------------------------------------------------------------------------*/
-    pixel retval;
-
-    pixval const red = 
-        composeComponents(PPM_GETR(pixelA), PPM_GETR(pixelB), distrib, maxval);
-    pixval const grn =
-        composeComponents(PPM_GETG(pixelA), PPM_GETG(pixelB), distrib, maxval);
-    pixval const blu = 
-        composeComponents(PPM_GETB(pixelA), PPM_GETB(pixelB), distrib, maxval);
-
-    PPM_ASSIGN(retval, red, grn, blu);
-
-    return retval;
-}
-
-
-
-static void
-composite(int      const originleft, 
-          int      const origintop, 
-          pixel ** const overlayImage, 
-          int      const overlayCols, 
-          int      const overlayRows,
-          xelval   const overlayMaxval, 
-          int      const overlayType,
-          int      const cols, 
-          int      const rows, 
-          xelval   const maxval, 
-          int      const type,
-          gray **  const alpha, 
-          gray     const alphaMax, 
-          bool     const invertAlpha,
-          float    const opacity,
-          FILE *   const ifp, 
-          FILE *   const ofp) {
-/*----------------------------------------------------------------------------
-   Overlay the overlay image 'overlayImage' onto the underlying image
-   which is in file 'ifp', and output the composite to file 'ofp'.
-
-   The underlying image file 'ifp' is positioned after its header.  The
-   width, height, format, and maxval of the underlying image are 'cols',
-   'rows', 'type', and 'maxval'.
-
-   The width, height, format, and maxval of the overlay image are
-   overlayCols, overlayRows, overlayType and overlayMaxval.
-
-   'originleft' and 'origintop' are the coordinates in the underlying
-   image plane where the top left corner of the overlay image is
-   to go.  It is not necessarily inside the underlying image (in fact,
-   may be negative).  Only the part of the overlay that actually intersects
-   the underlying image, if any, gets into the output.
-
-   Note that we modify the overlay image 'overlayImage' to change its
-   format and maxval to the format and maxval of the output.
------------------------------------------------------------------------------*/
-    /* otype and oxmaxv are the type and maxval for the composed (output)
-       image, and are derived from that of the underlying and overlay
-       images.
-    */
-    int    const otype = (overlayType < type) ? type : overlayType;
-    xelval const omaxv = pm_lcm(maxval, overlayMaxval, 1, PNM_OVERALLMAXVAL);
-
-    int     row;
-    xel     *pixelrow;
-
-    pixelrow = pnm_allocrow(cols);
-
-    if (overlayType != otype || overlayMaxval != omaxv) {
-        pnm_promoteformat(overlayImage, overlayCols, overlayRows,
-                          overlayMaxval, overlayType, omaxv, otype);
-    }
-
-    pnm_writepnminit(ofp, cols, rows, omaxv, otype, 0);
-
-    for (row = 0; row < rows; ++row) {
-        int col;
-
-        /* Read a row and convert it to the output type */
-        pnm_readpnmrow(ifp, pixelrow, cols, maxval, type);
-
-        if (type != otype || maxval != omaxv)
-            pnm_promoteformatrow(pixelrow, cols, maxval, type, omaxv, otype);
-
-        /* Now overlay the overlay with alpha (if defined) */
-        for (col = 0; col < cols; ++col) {
-            int const ovlcol = col - originleft;
-            int const ovlrow = row - origintop;
-
-            double overlayWeight;
-
-            if (ovlcol >= 0 && ovlcol < overlayCols &&
-                ovlrow >= 0 && ovlrow < overlayRows) {
-
-                if (alpha == NULL) {
-                    overlayWeight = opacity;
-                } else {
-                    double alphaval;
-                    alphaval = 
-                        (double)alpha[ovlrow][ovlcol] / (double)alphaMax;
-                    if (invertAlpha)
-                        alphaval = 1.0 - alphaval;
-                    overlayWeight = alphaval * opacity;
-                }
-
-                pixelrow[col] = composePixels(overlayImage[ovlrow][ovlcol],
-                                              pixelrow[col], 
-                                              overlayWeight, omaxv);
-            }
-        }
-        pnm_writepnmrow(ofp, pixelrow, cols, omaxv, otype, 0);
-    }
-    pnm_freerow(pixelrow);
-}
-
-
-
-int
-main(int argc, char *argv[]) {
-
-    FILE    *ifp, *ofp;
-    pixel   **image;
-    int     imageCols, imageRows, imageType;
-    xelval  imageMax;
-    int     cols, rows, type;
-    xelval  maxval;
-    gray    **alpha;
-    int     alphaCols, alphaRows;
-    xelval  alphaMax;
-    struct cmdlineInfo cmdline;
-    int originLeft, originTop;
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-        
-    { /* Read the overlay image into 'image' */
-        FILE *fp;
-        fp = pm_openr(cmdline.overlayFilespec);
-        image = 
-            pnm_readpnm(fp, &imageCols, &imageRows, &imageMax, &imageType);
-        pm_close(fp);
-    }
-    if (cmdline.alphaFilespec) {
-        /* Read the alpha mask file into 'alpha' */
-        FILE *fp = pm_openr(cmdline.alphaFilespec);
-        alpha = pgm_readpgm(fp, &alphaCols, &alphaRows, &alphaMax);
-        pm_close(fp);
-            
-        if (imageCols != alphaCols || imageRows != alphaRows)
-            pm_error("Alpha map and overlay image are not the same size");
-    } else
-        alpha = NULL;
-
-    ifp = pm_openr(cmdline.underlyingFilespec);
-
-    ofp = pm_openw(cmdline.outputFilespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &type);
-
-    computeOverlayPosition(cols, rows, imageCols, imageRows, 
-                           cmdline, &originLeft, &originTop);
-
-    composite(originLeft, originTop,
-              image, imageCols, imageRows, imageMax, imageType, 
-              cols, rows, maxval, type, 
-              alpha, alphaMax, cmdline.alphaInvert, cmdline.opacity,
-              ifp, ofp);
-
-    pm_close(ifp);
-    pm_close(ofp);
-
-    /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
-    return 0;
-}
-
-
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index 105f4644..485fa0b8 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -27,6 +27,16 @@
 #include "pam.h"
 
 
+
+static sample const
+clipSample(sample const unclipped,
+           sample const maxval) {
+
+    return MIN(maxval, MAX(0, unclipped));
+}
+
+
+
 static void
 validateKernelDimensions(unsigned int const width,
                          unsigned int const height) {
@@ -67,6 +77,7 @@ struct cmdlineInfo {
     unsigned int matrixSpec;
     struct matrixOpt matrix;
     unsigned int normalize;
+    unsigned int bias;
 };
 
 
@@ -304,6 +315,7 @@ parseCommandLine(int argc, char ** argv,
     unsigned int option_def_index;
     unsigned int matrixfileSpec;
     const char * matrixOpt;
+    unsigned int biasSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -316,6 +328,8 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->nooffset,       0);
     OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
             &cmdlineP->normalize,      0);
+    OPTENT3(0, "bias",         OPT_UINT,   &cmdlineP->bias,
+            &biasSpec,                 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -324,6 +338,9 @@ 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 (!biasSpec)
+        cmdlineP->bias = 0;
+
     if (matrixfileSpec && cmdlineP->matrixSpec)
         pm_error("You can't specify by -matrix and -matrixfile");
 
@@ -375,7 +392,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-struct convKernel {
+struct ConvKernel {
     unsigned int cols;
         /* Width of the convolution window */
     unsigned int rows;
@@ -393,12 +410,18 @@ struct convKernel {
            It can have magnitude greater than or less than one.  It can be
            positive or negative.  
         */
+    unsigned int bias;
+        /* The amount to be added to the linear combination of sample values.
+           We take a little liberty with the term "convolution kernel" to
+           include this value, since convolution per se does not involve any
+           such biasing.
+        */
 };
 
 
 
 static void
-warnBadKernel(struct convKernel * const convKernelP) {
+warnBadKernel(struct ConvKernel * const convKernelP) {
 
     float sum[3];
     unsigned int plane;
@@ -456,7 +479,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
                     tuple * const *      const ctuples, 
                     unsigned int         const depth,
                     bool                 const offsetPnm,
-                    struct convKernel ** const convKernelPP) {
+                    struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Compute the convolution matrix in normalized form from the PGM form
    'ctuples'/'cpamP'.  Each element of the output matrix is the actual weight
@@ -479,7 +502,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
     double const offset = offsetPnm ? - 1.0 : 0.0;
     unsigned int const planes = MIN(3, depth);
 
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     unsigned int plane;
 
     MALLOCVAR_NOFAIL(convKernelP);
@@ -507,13 +530,15 @@ convKernelCreatePnm(struct pam *         const cpamP,
             }
         }
     }
+    convKernelP->bias = 0;
+
     *convKernelPP = convKernelP;
 }
 
 
 
 static void
-convKernelDestroy(struct convKernel * const convKernelP) {
+convKernelDestroy(struct ConvKernel * const convKernelP) {
 
     unsigned int plane;
 
@@ -531,7 +556,7 @@ convKernelDestroy(struct convKernel * const convKernelP) {
 
 
 static void
-normalizeKernelPlane(struct convKernel * const convKernelP,
+normalizeKernelPlane(struct ConvKernel * const convKernelP,
                      unsigned int        const plane) {
 
     unsigned int row;
@@ -563,7 +588,7 @@ normalizeKernelPlane(struct convKernel * const convKernelP,
 
 
 static void
-normalizeKernel(struct convKernel * const convKernelP) {
+normalizeKernel(struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
    Modify *convKernelP by scaling every weight in a plane by the same factor
    such that the weights in the plane all add up to 1.
@@ -580,7 +605,7 @@ static void
 getKernelPnm(const char *         const fileName,
              unsigned int         const depth,
              bool                 const offset,
-             struct convKernel ** const convKernelPP) {
+             struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Get the convolution kernel from the PNM file named 'fileName'.
    'offset' means the PNM convolution matrix is defined in offset form so
@@ -613,7 +638,8 @@ static void
 convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
                           bool                 const normalize,
                           unsigned int         const depth,
-                          struct convKernel ** const convKernelPP) {
+                          unsigned int         const bias,
+                          struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Create a convolution kernel as described by a -matrix command line
    option.
@@ -625,7 +651,7 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
    so they may form a biased matrix -- i.e. one which brightens or dims the
    image overall.
 -----------------------------------------------------------------------------*/
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     unsigned int plane;
 
     MALLOCVAR(convKernelP);
@@ -652,6 +678,8 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
     if (normalize)
         normalizeKernel(convKernelP);
 
+    convKernelP->bias = bias;
+
     *convKernelPP = convKernelP;
 }
 
@@ -821,7 +849,8 @@ static void
 convKernelCreateSimpleFile(const char **        const fileNameList,
                            bool                 const normalize,
                            unsigned int         const depth,
-                           struct convKernel ** const convKernelPP) {
+                           unsigned int         const bias,
+                           struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Create a convolution kernel as described by a convolution matrix file.
    This is the simple file with floating point numbers in it, not the
@@ -834,7 +863,7 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
    so they may form a biased matrix -- i.e. one which brightens or dims the
    image overall.
 -----------------------------------------------------------------------------*/
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     unsigned int fileCt;
     unsigned int planeCt;
     unsigned int plane;
@@ -887,6 +916,8 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
 
     convKernelP->cols = width;
     convKernelP->rows = height;
+    convKernelP->bias = bias;
+
     *convKernelPP = convKernelP;
 }
 
@@ -895,7 +926,7 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
 static void
 getKernel(struct cmdlineInfo   const cmdline,
           unsigned int         const depth,
-          struct convKernel ** const convKernelPP) {
+          struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Figure out what the convolution kernel is.  It can come from various
    sources in various forms, as described on the command line, represented
@@ -904,17 +935,17 @@ getKernel(struct cmdlineInfo   const cmdline,
    We generate a kernel object in standard form (free of any indication of
    where it came from) and return a handle to it as *convKernelPP.
 ----------------------------------------------------------------------------*/
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
 
     if (cmdline.pnmMatrixFileName)
         getKernelPnm(cmdline.pnmMatrixFileName, depth, !cmdline.nooffset,
                      &convKernelP);
     else if (cmdline.matrixfile)
         convKernelCreateSimpleFile(cmdline.matrixfile, cmdline.normalize,
-                                   depth, &convKernelP);
+                                   depth, cmdline.bias, &convKernelP);
     else if (cmdline.matrixSpec)
         convKernelCreateMatrixOpt(cmdline.matrix, cmdline.normalize,
-                                  depth, &convKernelP);
+                                  depth, cmdline.bias, &convKernelP);
 
     warnBadKernel(convKernelP);
 
@@ -925,7 +956,7 @@ getKernel(struct cmdlineInfo   const cmdline,
 
 static void
 validateEnoughImageToConvolve(const struct pam *        const inpamP,
-                              const struct convKernel * const convKernelP) {
+                              const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
    Abort program if the image isn't big enough in both directions to have
    at least one convolved pixel.
@@ -1025,8 +1056,39 @@ readAndScaleRows(struct pam *              const inpamP,
 
 
 static void
+writePamRowBiased(struct pam * const outpamP,
+                  tuple *      const row,
+                  unsigned int const bias) {
+/*----------------------------------------------------------------------------
+   Write row[] to the output file according to *outpamP, but with
+   'bias' added to each sample value, clipped to maxval.
+-----------------------------------------------------------------------------*/
+    if (bias == 0)
+        pnm_writepamrow(outpamP, row);
+    else {
+        unsigned int col;
+
+        tuple * const outrow = pnm_allocpamrow(outpamP);
+
+        for (col = 0; col < outpamP->width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < outpamP->depth; ++plane) {
+                outrow[col][plane] =
+                    MIN(outpamP->maxval, bias + row[col][plane]);
+            }
+        }
+        pnm_writepamrow(outpamP, outrow);
+
+        pnm_freepamrow(outrow);
+    }
+}
+
+
+
+static void
 writeUnconvolvedTop(struct pam *              const outpamP,
-                    const struct convKernel * const convKernelP,
+                    const struct ConvKernel * const convKernelP,
                     tuple **                  const rowbuf) {
 /*----------------------------------------------------------------------------
    Write out the top part that we can't convolve because the convolution
@@ -1038,14 +1100,14 @@ writeUnconvolvedTop(struct pam *              const outpamP,
     unsigned int row;
 
     for (row = 0; row < convKernelP->rows/2; ++row)
-        pnm_writepamrow(outpamP, rowbuf[row]);
+        writePamRowBiased(outpamP, rowbuf[row], convKernelP->bias);
 }
 
 
 
 static void
 writeUnconvolvedBottom(struct pam *              const outpamP,
-                       const struct convKernel * const convKernelP,
+                       const struct ConvKernel * const convKernelP,
                        unsigned int              const windowHeight,
                        tuple **                  const circMap) {
 /*----------------------------------------------------------------------------
@@ -1061,7 +1123,7 @@ writeUnconvolvedBottom(struct pam *              const outpamP,
          row < windowHeight;
          ++row) {
 
-        pnm_writepamrow(outpamP, circMap[row]);
+        writePamRowBiased(outpamP, circMap[row], convKernelP->bias);
     }
 }
 
@@ -1093,7 +1155,7 @@ setupCircMap(tuple **     const circMap,
 static void
 convolveGeneralRowPlane(struct pam *              const pamP,
                         tuple **                  const window,
-                        const struct convKernel * const convKernelP,
+                        const struct ConvKernel * const convKernelP,
                         unsigned int              const plane,
                         tuple *                   const outputrow) {
 /*----------------------------------------------------------------------------
@@ -1114,7 +1176,10 @@ convolveGeneralRowPlane(struct pam *              const pamP,
     
     for (col = 0; col < pamP->width; ++col) {
         if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
         else {
             unsigned int const leftcol = col - ccolso2;
             unsigned int crow;
@@ -1127,7 +1192,8 @@ convolveGeneralRowPlane(struct pam *              const pamP,
                     sum += leftrptr[ccol][plane] *
                         convKernelP->weight[plane][crow][ccol];
             }
-            outputrow[col][plane] = MIN(pamP->maxval, MAX(0, sum + 0.5));
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + sum + 0.5, pamP->maxval);
         }
     }
 }
@@ -1137,7 +1203,7 @@ convolveGeneralRowPlane(struct pam *              const pamP,
 static void
 convolveGeneral(struct pam *              const inpamP,
                 struct pam *              const outpamP,
-                const struct convKernel * const convKernelP) {
+                const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
    Do the convolution without taking advantage of any useful redundancy in the
    convolution matrix.
@@ -1244,7 +1310,7 @@ freeSum(sample **    const sum,
 static void
 computeInitialColumnSums(struct pam *              const pamP,
                          tuple **                  const window,
-                         const struct convKernel * const convKernelP,
+                         const struct ConvKernel * const convKernelP,
                          sample **                 const convColumnSum) {
 /*----------------------------------------------------------------------------
   Add up the sum of each column of window[][], whose rows are described
@@ -1271,7 +1337,7 @@ computeInitialColumnSums(struct pam *              const pamP,
 
 
 static void
-convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
+convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
                               struct pam *              const pamP,
                               tuple **                  const window,
                               tuple *                   const outputrow,
@@ -1301,30 +1367,32 @@ convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
         unsigned int col;
         sample gisum;
 
-        gisum = 0;
-        for (col = 0; col < pamP->width; ++col) {
-            if (col < ccolso2 || col >= pamP->width - ccolso2)
-                outputrow[col][plane] = window[crowso2][col][plane];
-            else if (col == ccolso2) {
-                unsigned int const leftcol = col - ccolso2;
-
-                unsigned int ccol;
-
-                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
-                    gisum += convColumnSum[plane][leftcol + ccol];
-
+        for (col = 0, gisum = 0; col < pamP->width; ++col) {
+            if (col < ccolso2 || col >= pamP->width - ccolso2) {
+                /* The unconvolved left or right edge */
                 outputrow[col][plane] =
-                    MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                    clipSample(convKernelP->bias +
+                               window[crowso2][col][plane],
+                               pamP->maxval);
             } else {
-                /* Column numbers to subtract or add to isum */
-                unsigned int const subcol = col - ccolso2 - 1;
-                unsigned int const addcol = col + ccolso2;  
+                if (col == ccolso2) {
+                    unsigned int const leftcol = col - ccolso2;
 
-                gisum -= convColumnSum[plane][subcol];
-                gisum += convColumnSum[plane][addcol];
+                    unsigned int ccol;
 
+                    for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                        gisum += convColumnSum[plane][leftcol + ccol];
+                } else {
+                    /* Column numbers to subtract or add to isum */
+                    unsigned int const subcol = col - ccolso2 - 1;
+                    unsigned int const addcol = col + ccolso2;  
+
+                    gisum -= convColumnSum[plane][subcol];
+                    gisum += convColumnSum[plane][addcol];
+                }
                 outputrow[col][plane] =
-                    MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                    clipSample(convKernelP->bias + gisum * weight + 0.5,
+                               pamP->maxval);
             }
         }
     }
@@ -1334,7 +1402,7 @@ convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
 
 static void
 convolveRowWithColumnSumsVertical(
-    const struct convKernel * const convKernelP,
+    const struct ConvKernel * const convKernelP,
     struct pam *              const pamP,
     tuple **                  const window,
     tuple *                   const outputrow,
@@ -1363,9 +1431,13 @@ convolveRowWithColumnSumsVertical(
         unsigned int col;
     
         for (col = 0; col < pamP->width; ++col) {
-            if (col < ccolso2 || col >= pamP->width - ccolso2)
-                outputrow[col][plane] = window[crowso2][col][plane];
-            else {
+            if (col < ccolso2 || col >= pamP->width - ccolso2) {
+                /* The unconvolved left or right edge */
+                outputrow[col][plane] =
+                    clipSample(convKernelP->bias +
+                               window[crowso2][col][plane],
+                               pamP->maxval);
+            } else {
                 unsigned int const leftcol = col - ccolso2;
                 unsigned int ccol;
                 float sum;
@@ -1376,7 +1448,8 @@ convolveRowWithColumnSumsVertical(
                     sum += convColumnSum[plane][leftcol + ccol] *
                         convKernelP->weight[plane][0][ccol];
 
-                outputrow[col][plane] = MIN(pamP->maxval, MAX(0, sum + 0.5));
+                outputrow[col][plane] =
+                    clipSample(convKernelP->bias + sum + 0.5, pamP->maxval);
             }
         }
     }
@@ -1387,7 +1460,7 @@ convolveRowWithColumnSumsVertical(
 static void
 convolveMeanRowPlane(struct pam *              const pamP,
                      tuple **                  const window,
-                     const struct convKernel * const convKernelP,
+                     const struct ConvKernel * const convKernelP,
                      unsigned int              const plane,
                      tuple *                   const outputrow,
                      sample *                  const convColumnSum) {
@@ -1417,38 +1490,40 @@ convolveMeanRowPlane(struct pam *              const pamP,
     unsigned int col;
     sample gisum;
 
-    gisum = 0;
-    for (col = 0; col < pamP->width; ++col) {
-        if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
-        else if (col == ccolso2) {
-            unsigned int const leftcol = col - ccolso2;
-
-            unsigned int ccol;
-
-            for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
-                sample * const thisColumnSumP =
-                    &convColumnSum[leftcol + ccol];
-                *thisColumnSumP = *thisColumnSumP
-                    - window[subrow][ccol][plane]
-                    + window[addrow][ccol][plane];
-                gisum += *thisColumnSumP;
-            }
+    for (col = 0, gisum = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
         } else {
-            /* Column numbers to subtract or add to isum */
-            unsigned int const subcol = col - ccolso2 - 1;
-            unsigned int const addcol = col + ccolso2;  
-
-            convColumnSum[addcol] = convColumnSum[addcol]
-                - window[subrow][addcol][plane]
-                + window[addrow][addcol][plane];
+            if (col == ccolso2) {
+                unsigned int const leftcol = col - ccolso2;
 
-            gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
+                unsigned int ccol;
 
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                    sample * const thisColumnSumP =
+                        &convColumnSum[leftcol + ccol];
+                    *thisColumnSumP = *thisColumnSumP
+                        - window[subrow][ccol][plane]
+                        + window[addrow][ccol][plane];
+                    gisum += *thisColumnSumP;
+                }
+            } else {
+                /* Column numbers to subtract or add to isum */
+                unsigned int const subcol = col - ccolso2 - 1;
+                unsigned int const addcol = col + ccolso2;  
+                
+                convColumnSum[addcol] = convColumnSum[addcol]
+                    - window[subrow][addcol][plane]
+                    + window[addrow][addcol][plane];
+                
+                gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
+            }
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                clipSample(convKernelP->bias + gisum * weight + 0.5,
+                           pamP->maxval);
         }
     }
 }
@@ -1457,7 +1532,7 @@ convolveMeanRowPlane(struct pam *              const pamP,
 
 typedef void convolver(struct pam *              const inpamP,
                        struct pam *              const outpamP,
-                       const struct convKernel * const convKernelP);
+                       const struct ConvKernel * const convKernelP);
 
 
 
@@ -1466,7 +1541,7 @@ static convolver convolveMean;
 static void
 convolveMean(struct pam *              const inpamP,
              struct pam *              const outpamP,
-             const struct convKernel * const convKernelP) {
+             const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
   Mean Convolution
 
@@ -1645,7 +1720,7 @@ freeRowSum(sample ***   const sum,
 static void
 convolveHorizontalRowPlane0(struct pam *              const outpamP,
                             tuple **                  const window,
-                            const struct convKernel * const convKernelP,
+                            const struct ConvKernel * const convKernelP,
                             unsigned int              const plane,
                             tuple *                   const outputrow,
                             sample **                 const sumWindow) {
@@ -1659,47 +1734,57 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
     unsigned int col;
 
     for (col = 0; col < outpamP->width; ++col) {
-        if (col < ccolso2 || col >= outpamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
-        else if (col == ccolso2) {
-            /* This is the first column for which the entire convolution
-               kernel fits within the image horizontally.  I.e. the window
-               starts at the left edge of the image.
-            */
-            unsigned int const leftcol = 0;
-            
+        if (col < ccolso2 || col >= outpamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           outpamP->maxval);
+        } else {
             float matrixSum;
-            unsigned int crow;
 
-            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
-                tuple * const tuplesInWindow = &window[crow][leftcol];
+            if (col == ccolso2) {
+                /* This is the first column for which the entire convolution
+                   kernel fits within the image horizontally.  I.e. the window
+                   starts at the left edge of the image.
+                */
+                unsigned int const leftcol = 0;
+            
+                unsigned int crow;
 
-                unsigned int ccol;
-                
-                sumWindow[crow][col] = 0;
-                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
-                    sumWindow[crow][col] += tuplesInWindow[ccol][plane];
-                matrixSum +=
-                    sumWindow[crow][col] * convKernelP->weight[plane][crow][0];
-            }
-            outputrow[col][plane] =
-                MIN(outpamP->maxval, MAX(0, matrixSum + 0.5));
-        } else {
-            unsigned int const subcol  = col - ccolso2 - 1;
-            unsigned int const addcol  = col + ccolso2;
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    tuple * const tuplesInWindow = &window[crow][leftcol];
 
-            float matrixSum;
-            unsigned int crow;
+                    unsigned int ccol;
+                
+                    sumWindow[crow][col] = 0;
+                    for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                        sumWindow[crow][col] += tuplesInWindow[ccol][plane];
+                    matrixSum +=
+                        sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
+                }
+            } else {
+                unsigned int const subcol  = col - ccolso2 - 1;
+                unsigned int const addcol  = col + ccolso2;
 
-            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
-                sumWindow[crow][col] = sumWindow[crow][col-1] +
-                    + window[crow][addcol][plane]
-                    - window[crow][subcol][plane];
-                matrixSum +=
-                    sumWindow[crow][col] * convKernelP->weight[plane][crow][0];
+                unsigned int crow;
+                
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    sumWindow[crow][col] = sumWindow[crow][col-1] +
+                        + window[crow][addcol][plane]
+                        - window[crow][subcol][plane];
+                    matrixSum +=
+                        sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
+                }
             }
             outputrow[col][plane] =
-                MIN(outpamP->maxval, MAX(0, matrixSum + 0.5));
+                clipSample(convKernelP->bias + matrixSum + 0.5,
+                           outpamP->maxval);
         }
     }
 }
@@ -1737,7 +1822,7 @@ setupCircMap2(tuple **     const rowbuf,
 static void
 convolveHorizontalRowPlane(struct pam *              const pamP,
                            tuple **                  const window,
-                           const struct convKernel * const convKernelP,
+                           const struct ConvKernel * const convKernelP,
                            unsigned int              const plane,
                            tuple *                   const outputrow,
                            sample **                 const sumWindow) {
@@ -1763,14 +1848,16 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
-        if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
-        else if (col == ccolso2) {
+        float matrixSum;
+
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
+        } else if (col == ccolso2) {
             unsigned int const leftcol = 0;
                 /* Window is up againt left edge of image */
 
-            float matrixSum;
-
             {
                 unsigned int ccol;
                 sumWindow[newrow][col] = 0;
@@ -1787,13 +1874,10 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
                         convKernelP->weight[plane][crow][0];
                 }
             }
-            outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         } else {
             unsigned int const subcol  = col - ccolso2 - 1;
             unsigned int const addcol  = col + ccolso2;  
 
-            float matrixSum;
             unsigned int crow;
 
             sumWindow[newrow][col] =
@@ -1805,9 +1889,9 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
                 matrixSum += sumWindow[crow][col] *
                     convKernelP->weight[plane][crow][0];
             }
-            outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         }
+        outputrow[col][plane] =
+            clipSample(convKernelP->bias + matrixSum + 0.5, pamP->maxval);
     }
 }
 
@@ -1818,7 +1902,7 @@ static convolver convolveHorizontal;
 static void
 convolveHorizontal(struct pam *              const inpamP,
                    struct pam *              const outpamP,
-                   const struct convKernel * const convKernelP) {
+                   const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
   Horizontal Convolution
 
@@ -1909,7 +1993,7 @@ convolveHorizontal(struct pam *              const inpamP,
 static void
 convolveVerticalRowPlane(struct pam *              const pamP,
                          tuple **                  const circMap,
-                         const struct convKernel * const convKernelP,
+                         const struct ConvKernel * const convKernelP,
                          unsigned int              const plane,
                          tuple *                   const outputrow,
                          sample *                  const convColumnSum) {
@@ -1927,45 +2011,54 @@ convolveVerticalRowPlane(struct pam *              const pamP,
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
-        if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = circMap[crowso2][col][plane];
-        else if (col == ccolso2) {
-            unsigned int const leftcol = 0;
-                /* Convolution window is againt left edge of image */
-
-            float matrixSum;
-            unsigned int ccol;
-
-            /* Slide window down in the first kernel's worth of columns */
-            for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
-                convColumnSum[leftcol + ccol] +=
-                    circMap[addrow][leftcol + ccol][plane];
-                convColumnSum[leftcol + ccol] -=
-                    circMap[subrow][leftcol + ccol][plane];
-            }
-            for (ccol = 0, matrixSum = 0.0; ccol < convKernelP->cols; ++ccol) {
-                matrixSum += convColumnSum[leftcol + ccol] *
-                    convKernelP->weight[plane][0][ccol];
-            }
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
+                clipSample(convKernelP->bias + circMap[crowso2][col][plane],
+                           pamP->maxval);
         } else {
-            unsigned int const leftcol = col - ccolso2;
-            unsigned int const addcol  = col + ccolso2;
-
             float matrixSum;
-            unsigned int ccol;
 
-            /* Slide window down in the column that just entered the window */
-            convColumnSum[addcol] += circMap[addrow][addcol][plane];
-            convColumnSum[addcol] -= circMap[subrow][addcol][plane];
+            if (col == ccolso2) {
+                unsigned int const leftcol = 0;
+                    /* Convolution window is againt left edge of image */
+
+                unsigned int ccol;
 
-            for (ccol = 0, matrixSum = 0.0; ccol < convKernelP->cols; ++ccol) {
-                matrixSum += convColumnSum[leftcol + ccol] *
-                    convKernelP->weight[plane][0][ccol];
+                /* Slide window down in the first kernel's worth of columns */
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                    convColumnSum[leftcol + ccol] +=
+                        circMap[addrow][leftcol + ccol][plane];
+                    convColumnSum[leftcol + ccol] -=
+                        circMap[subrow][leftcol + ccol][plane];
+                }
+                for (ccol = 0, matrixSum = 0.0;
+                     ccol < convKernelP->cols;
+                     ++ccol) {
+                    matrixSum += convColumnSum[leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+                }
+            } else {
+                unsigned int const leftcol = col - ccolso2;
+                unsigned int const addcol  = col + ccolso2;
+
+                unsigned int ccol;
+
+                /* Slide window down in the column that just entered the
+                   window
+                */
+                convColumnSum[addcol] += circMap[addrow][addcol][plane];
+                convColumnSum[addcol] -= circMap[subrow][addcol][plane];
+
+                for (ccol = 0, matrixSum = 0.0;
+                     ccol < convKernelP->cols;
+                     ++ccol) {
+                    matrixSum += convColumnSum[leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+                }
             }
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
+                clipSample(convKernelP->bias + matrixSum + 0.5, pamP->maxval);
         }
     }
 }
@@ -1977,7 +2070,7 @@ static convolver convolveVertical;
 static void
 convolveVertical(struct pam *              const inpamP,
                  struct pam *              const outpamP,
-                 const struct convKernel * const convKernelP) {
+                 const struct ConvKernel * const convKernelP) {
 
     /* Uses column sums as in mean convolution, above */
 
@@ -2058,7 +2151,7 @@ struct convolveType {
 
 
 static bool
-convolutionIncludesHorizontal(const struct convKernel * const convKernelP) {
+convolutionIncludesHorizontal(const struct ConvKernel * const convKernelP) {
 
     bool horizontal;
     unsigned int row;
@@ -2086,7 +2179,7 @@ convolutionIncludesHorizontal(const struct convKernel * const convKernelP) {
 
 
 static bool
-convolutionIncludesVertical(const struct convKernel * const convKernelP) {
+convolutionIncludesVertical(const struct ConvKernel * const convKernelP) {
 
     bool vertical;
     unsigned int col;
@@ -2114,7 +2207,7 @@ convolutionIncludesVertical(const struct convKernel * const convKernelP) {
 
 
 static void
-determineConvolveType(const struct convKernel * const convKernelP,
+determineConvolveType(const struct ConvKernel * const convKernelP,
                       struct convolveType *     const typeP) {
 /*----------------------------------------------------------------------------
    Determine which form of convolution is best to convolve the kernel
@@ -2150,7 +2243,7 @@ main(int argc, char * argv[]) {
     struct cmdlineInfo cmdline;
     FILE * ifP;
     struct convolveType convolveType;
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     struct pam inpam;
     struct pam outpam;
 
diff --git a/editor/pnmmontage.c b/editor/pnmmontage.c
index 47827610..e54afc45 100644
--- a/editor/pnmmontage.c
+++ b/editor/pnmmontage.c
@@ -10,6 +10,7 @@
  * implied warranty.
  */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
 #include <limits.h>
diff --git a/editor/ppmcolormask.c b/editor/ppmcolormask.c
index 4e462f3e..31fbff2a 100644
--- a/editor/ppmcolormask.c
+++ b/editor/ppmcolormask.c
@@ -9,6 +9,7 @@
   Contributed to the public domain by its author.
 =========================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
 #include <string.h>
diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c
index c733ffcb..63d781ec 100644
--- a/editor/ppmdraw.c
+++ b/editor/ppmdraw.c
@@ -1,5 +1,6 @@
-#define _XOPEN_SOURCE    /* Make sure M_PI is in math.h */
-#define _BSD_SOURCE      /* Make sure strdup is in string.h */
+#define _XOPEN_SOURCE 500 
+   /* Make sure M_PI is in math.h, strdup is in string.h */
+#define _BSD_SOURCE      /* Make sure strdup is in string.h (alternate) */
 
 #include <string.h>
 #include <ctype.h>
diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c
index 1909c93a..4ec9edaa 100644
--- a/editor/specialty/pnmindex.c
+++ b/editor/specialty/pnmindex.c
@@ -14,6 +14,7 @@
 
 ============================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is in string.h */
 
 #include <assert.h>
diff --git a/generator/Makefile b/generator/Makefile
index 3c30cdd0..d0ea6b60 100644
--- a/generator/Makefile
+++ b/generator/Makefile
@@ -14,9 +14,10 @@ include $(BUILDDIR)/config.mk
 # This package is so big, it's useful even when some parts won't 
 # build.
 
-PORTBINARIES = pamgauss pamgradient pamseq pamstereogram \
+PORTBINARIES = pamcrater pamgauss pamgradient \
+	       pamseq pamshadedrelief pamstereogram \
 	       pbmpage pbmmake pbmtext pbmtextps pbmupc \
-	       pgmcrater pgmkernel pgmmake pgmnoise pgmramp \
+	       pgmkernel pgmmake pgmnoise pgmramp \
 	       ppmcie ppmcolors ppmforge ppmmake ppmpat ppmrough ppmwheel \
 
 # We don't include programs that have special library dependencies in the
@@ -28,7 +29,7 @@ MERGEBINARIES = $(PORTBINARIES)
 
 
 BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
-SCRIPTS = ppmrainbow
+SCRIPTS = pgmcrater ppmrainbow
 
 OBJECTS = $(BINARIES:%=%.o)
 
diff --git a/generator/pamcrater.c b/generator/pamcrater.c
new file mode 100644
index 00000000..d61ce548
--- /dev/null
+++ b/generator/pamcrater.c
@@ -0,0 +1,428 @@
+/*=============================================================================
+                               pamcrater
+===============================================================================
+  Fractal cratering
+
+  This is derived from John Walker's 'pgmcrater' which not only creates
+  the terrain map as this program does, but then does a relief filter to
+  convert it to a shaded visual image.
+
+  The  algorithm  used  to  determine crater size is as described on
+  pages 31 and 32 of:
+
+  Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal
+      Images, New York: Springer Verlag, 1988.
+
+  The  mathematical  technique  used  to calculate crater radii that
+  obey the proper area law distribution from a uniformly distributed
+  pseudorandom sequence was developed by Rudy Rucker.
+
+  The original program carried this attribution and license:
+
+       Designed and implemented in November of 1989 by:
+
+        John Walker
+        Autodesk SA
+        Avenue des Champs-Montants 14b
+        CH-2074 MARIN
+        Switzerland
+        Usenet: kelvin@Autodesk.com
+        Fax:    038/33 88 15
+        Voice:  038/33 76 33
+
+  Permission  to  use, copy, modify, and distribute this software and
+  its documentation  for  any  purpose  and  without  fee  is  hereby
+  granted,  without any conditions or restrictions.  This software is
+  provided "as is" without express or implied warranty.
+
+=============================================================================*/
+
+/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at
+   right edge. Make craters wrap around the image (enables tiling of image).
+ */
+
+#define _XOPEN_SOURCE   /* get M_PI in math.h */
+
+#include <assert.h>
+#include <math.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "pam.h"
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int number;
+    unsigned int height;
+    unsigned int width;
+    unsigned int randomseedSpec;
+    unsigned int randomseed;
+    unsigned int test;
+    unsigned int radius;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    unsigned int numberSpec, heightSpec, widthSpec, radiusSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "number",     OPT_UINT,    &cmdlineP->number,
+            &numberSpec,                 0);
+    OPTENT3(0,   "height",     OPT_UINT,    &cmdlineP->height,
+            &heightSpec,                 0);
+    OPTENT3(0,   "width",      OPT_UINT,    &cmdlineP->width,
+            &widthSpec,                  0);
+    OPTENT3(0,   "randomseed", OPT_UINT,    &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec,   0);
+    OPTENT3(0,   "test",       OPT_FLAG,    NULL,
+            &cmdlineP->test,       0);
+    OPTENT3(0,   "radius",     OPT_UINT,    &cmdlineP->radius,
+            &radiusSpec,           0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 > 0)
+        pm_error("There are no non-option arguments.  You specified %u",
+                 argc-1);
+
+    if (!heightSpec)
+        cmdlineP->height = 256;
+
+    if (cmdlineP->height == 0)
+        pm_error("-height must be positive");
+
+    if (!widthSpec)
+        cmdlineP->width = 256;
+
+    if (cmdlineP->width == 0)
+        pm_error("-width must be positive");
+
+    if (cmdlineP->test) {
+        if (!radiusSpec)
+            pm_error("With -test, you must specify -radius");
+        else {
+            if(MAX(cmdlineP->height, cmdlineP->width) * 2 < cmdlineP->radius)
+                pm_error("Radius (%u) too large", cmdlineP->radius);
+
+            if (numberSpec)
+                pm_error("-number is meaningless with -test");
+
+            if (cmdlineP->randomseedSpec)
+                pm_error("-randomseed is meaningless with -test");
+        }
+    } else {
+        if (radiusSpec)
+            pm_error("-radius is meaningful only with -test");
+
+        if (!numberSpec)
+            cmdlineP->number = 50000;
+
+        if (cmdlineP->number == 0)
+            pm_error("-number must be positive");
+    }
+    free(option_def);
+}
+
+
+/* Definitions for obtaining random numbers. */
+
+/*  Display parameters  */
+
+static double const arand       = 32767.0;  /* Random number parameters */
+static double const CdepthPower = 1.5;      /* Crater depth power factor */
+static double const DepthBias2  = 0.5;      /* Square of depth bias */
+
+
+
+static double const
+cast(double const high) {
+
+    return high * ((rand() & 0x7FFF) / arand);
+}
+
+
+
+static unsigned int
+mod(int          const t,
+    unsigned int const n) {
+
+    /* This is used to transform coordinates beyond bounds into ones
+       within: craters "wrap around" the edges.  This enables tiling
+       of the image.
+
+       Produces strange effects when crater radius is very large compared
+       to image size.
+    */
+
+    int m;
+
+    m = t % (int)n;
+
+    if (m < 0)
+        m += n;
+
+    return m;
+}
+
+
+
+static sample *
+terrainModP(struct pam * const pamP,
+            tuple **     const terrain,
+            int          const x,
+            int          const y) {
+
+    return &terrain[mod(y, pamP->height)][mod(x, pamP->width)][0];
+}
+
+
+
+
+static sample
+terrainMod(struct pam * const pamP,
+           tuple **     const terrain,
+           int          const x,
+           int          const y) {
+
+    return *terrainModP(pamP, terrain, x, y);
+}
+
+
+
+static void
+smallCrater(struct pam * const pamP,
+            tuple **     const terrain,
+            int          const cx,
+            int          const cy,
+            double       const g) {
+/*----------------------------------------------------------------------------
+   Generate a crater with a special method for tiny craters.
+-----------------------------------------------------------------------------*/
+    int y;
+    unsigned int amptot;
+    unsigned int npatch;
+
+    /* Set pixel to the average of its Moore neighborhood. */
+
+    for (y = cy - 1, amptot = 0, npatch = 0; y <= cy + 1; ++y) {
+        int x;
+        for (x = cx - 1; x <= cx + 1; ++x) {
+            amptot += terrainMod(pamP, terrain, x, y);
+            ++npatch;
+        }
+    }
+    {
+        unsigned int const axelev = amptot / npatch;
+        
+        /* Perturb the mean elevation by a small random factor. */
+
+        int const x = g >= 1 ? ((rand() >> 8) & 3) - 1 : 0;
+        *terrainModP(pamP, terrain, cx, cy) = axelev + x;
+    }
+}
+
+
+
+static void
+normalCrater(struct pam * const pamP,
+             tuple **     const terrain,
+             int          const cx,
+             int          const cy,
+             double       const radius) {
+/*----------------------------------------------------------------------------
+   Generate a regular (not tiny) crater.
+
+   Generate an impact feature of the correct size and shape.
+-----------------------------------------------------------------------------*/
+    int    const impactRadius = (int) MAX(2, (radius / 3));
+    int    const craterRadius = (int) radius;
+    double const rollmin      = 0.9;
+
+    int y;
+    unsigned int amptot, axelev;
+    unsigned int npatch;
+
+    /* Determine mean elevation around the impact area.
+       We assume the impact area is a fraction of the total crater size. */
+
+    for (y = cy - impactRadius, amptot = 0, npatch = 0;
+         y <= cy + impactRadius;
+         ++y) {
+        int x;
+        for (x = cx - impactRadius; x <= cx + impactRadius; ++x) {
+            amptot += terrainMod(pamP, terrain, x, y);
+            ++npatch;
+        }
+    }
+    assert(npatch > 0);
+    axelev = amptot / npatch;
+
+    for (y = cy - craterRadius; y <= cy + craterRadius; ++y) {
+        int const dysq = (cy - y) * (cy - y);
+
+        int x;
+
+        for (x = cx - craterRadius; x <= cx + craterRadius; ++x) {
+            int  const dxsq = (cx - x) * (cx - x);
+            double const cd = (dxsq + dysq) /
+                              (double) (craterRadius * craterRadius);
+            double const cd2 = cd * 2.25;
+            double const tcz = sqrt(DepthBias2) - sqrt(fabs(1 - cd2));
+            double cz;
+            double roll;
+
+            cz = MAX((cd2 > 1) ? 0.0 : -10, tcz);  /* Initial value */
+
+            cz *= pow(craterRadius, CdepthPower);
+            if (dysq == 0 && dxsq == 0 && ((int) cz) == 0) {
+                cz = cz < 0 ? -1 : 1;
+            }
+
+            roll = (((1 / (1 - MIN(rollmin, cd))) /
+                     (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin;
+
+            {
+                unsigned int av;
+                av = (axelev + cz) * (1 - roll) +
+                    (terrainMod(pamP, terrain, x, y) + cz) * roll;
+                av = MAX(1000, MIN(64000, av));
+                
+                *terrainModP(pamP, terrain, x, y) = av;
+            }
+        }
+    }
+}
+
+
+
+/* We should also have largeCrater() */
+
+
+
+static void
+plopCrater(struct pam * const pamP,
+           tuple **     const terrain,
+           int          const cx,
+           int          const cy,
+           double       const radius) {
+
+    if (radius < 3)
+        smallCrater (pamP, terrain, cx, cy, radius);
+    else
+        normalCrater(pamP, terrain, cx, cy, radius);
+}
+
+
+
+static void
+genCraters(struct CmdlineInfo const cmdline) {
+/*----------------------------------------------------------------------------
+   Generate cratered terrain
+-----------------------------------------------------------------------------*/
+    tuple ** terrain;    /* elevation array */
+    unsigned int row;
+    struct pam pam;
+
+    /* Allocate the elevation array and initialize it to mean surface
+       elevation.
+    */
+
+    pam.size   = sizeof(pam);
+    pam.len    = PAM_STRUCT_SIZE(tuple_type);
+    pam.file   = stdout;
+    pam.format = PAM_FORMAT;
+    pam.height = cmdline.height;
+    pam.width  = cmdline.width;
+    pam.depth  = 1;
+    pam.maxval = 65535;
+    pam.bytes_per_sample = 2;
+    STRSCPY(pam.tuple_type, "elevation");
+
+    terrain = pnm_allocpamarray(&pam);
+
+    for (row = 0; row < pam.height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pam.width; ++col)
+            terrain[row][col][0] = pam.maxval / 2;
+    }
+    
+    if (cmdline.test)
+        plopCrater(&pam, terrain,
+                   pam.width/2, pam.height/2, (double) cmdline.radius);
+    else {
+        unsigned int const ncraters = cmdline.number; /* num of craters */
+        unsigned int l;
+
+        for (l = 0; l < ncraters; ++l) {
+            int const cx = cast((double) pam.width  - 1);
+            int const cy = cast((double) pam.height - 1);
+
+            /* Thanks, Rudy, for this equation that maps the uniformly
+               distributed numbers from cast() into an area-law distribution
+               as observed on cratered bodies.
+
+               Produces values within the interval:
+               0.56419 <= radius <= 56.419
+            */
+            double const radius = sqrt(1 / (M_PI * (1 - cast(0.9999))));
+
+            plopCrater(&pam, terrain, cx, cy, radius);
+
+            if (((l + 1) % 100000) == 0)
+                pm_message("%u craters generated of %u (%u%% done)",
+                           l + 1, ncraters, ((l + 1) * 100) / ncraters);
+        }
+    }
+
+    pnm_writepam(&pam, terrain);
+
+    pnm_freepamarray(terrain, &pam);
+
+    pm_close(stdout);
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    genCraters(cmdline);
+
+    return 0;
+}
+
+
+
diff --git a/generator/pamshadedrelief.c b/generator/pamshadedrelief.c
new file mode 100644
index 00000000..89996c83
--- /dev/null
+++ b/generator/pamshadedrelief.c
@@ -0,0 +1,250 @@
+/*=============================================================================
+                               pamshaderelief
+===============================================================================
+  Generate a shaded relief image of terrain, given a terrain map - a two
+  dimensional map of elevations.  A shaded relief image is an image of
+  what terrain with the given elevations would look like illuminated by
+  oblique light.
+
+  The input array is a one-channel PAM image.  The sample values are
+  elevations of terrain.
+  
+  This is derived from John Walker's 'pgmcrater' which not only does this
+  shading, but first generates a terrain map of fractal craters on which to
+  run it.
+
+
+  The original program carried this attribution and license:
+
+       Designed and implemented in November of 1989 by:
+
+        John Walker
+        Autodesk SA
+        Avenue des Champs-Montants 14b
+        CH-2074 MARIN
+        Switzerland
+        Usenet: kelvin@Autodesk.com
+        Fax:    038/33 88 15
+        Voice:  038/33 76 33
+
+  Permission  to  use, copy, modify, and distribute this software and
+  its documentation  for  any  purpose  and  without  fee  is  hereby
+  granted,  without any conditions or restrictions.  This software is
+  provided "as is" without express or implied warranty.
+
+=============================================================================*/
+
+/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right
+   edge.
+*/
+
+#define _XOPEN_SOURCE   /* get M_PI in math.h */
+
+#include <assert.h>
+#include <math.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    float        gamma;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    unsigned int gammaSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "gamma",    OPT_FLOAT,   &cmdlineP->gamma,
+            &gammaSpec,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!gammaSpec)
+        cmdlineP->gamma = 1.0;
+
+    if (cmdlineP->gamma <= 0.0)
+        pm_error("gamma correction must be greater than 0");
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %u", argc-1);
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}
+
+
+
+/* Definitions for obtaining random numbers. */
+
+/*  Display parameters  */
+
+static double const ImageGamma = 0.5;     /* Inherent gamma of mapped image */
+static int    const slopemin   = -52;
+static int    const slopemax   = 52;
+
+
+
+static void
+generateSlopeGrayMap(sample * const slopeGrayMap,
+                     double   const dgamma) {
+/*----------------------------------------------------------------------------
+   Map each possible slope to the brightness that terrain with that
+   left-to-right slope should have in the shaded relief.
+
+   The brightness is what would result from light incident from the left
+   falling on the terrain.
+-----------------------------------------------------------------------------*/
+    double const gamma = dgamma * ImageGamma;
+
+    int i;
+
+    for (i = slopemin; i <= 0; ++i) {   /* Negative, downhill, dark */
+        slopeGrayMap[i - slopemin] =
+            128 - 127.0 * pow(sin((M_PI / 2) * i / slopemin), gamma);
+    }
+    for (i = 0; i <= slopemax; ++i) {   /* Positive, uphill, bright */
+        slopeGrayMap[i - slopemin] =
+            128 + 127.0 * pow(sin((M_PI / 2) * i / slopemax), gamma);
+    }
+
+    /* Confused?   OK,  we're using the  left-to-right slope to
+       calculate a shade based on the sine of  the  angle  with
+       respect  to the vertical (light incident from the left).
+       Then, with one exponentiation, we account for  both  the
+       inherent   gamma   of   the   image  (ad-hoc),  and  the
+       user-specified display gamma, using the identity:
+       (x^y)^z = (x^(y*z))
+    */
+}
+
+
+
+static gray
+brightnessOfSlope(int      const slope,
+                  sample * const slopeGrayMap) {
+
+    return slopeGrayMap[MIN(MAX(slopemin, slope), slopemax) - slopemin];
+}
+
+
+
+static void
+writeShadedRelief(struct pam * const terrainPamP,
+                  tuple **     const terrain,
+                  double       const dgamma,
+                  FILE *       const ofP) {
+
+    unsigned int row;
+    tuple * outrow;
+    sample * slopeGrayMap; /* Slope to gray value map */
+    struct pam outpam;
+
+    outpam.size   = sizeof(outpam);
+    outpam.len    = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file   = ofP;
+    outpam.format = PAM_FORMAT;
+    outpam.height = terrainPamP->height;
+    outpam.width  = terrainPamP->width;
+    outpam.depth  = 1;
+    outpam.maxval = 255;
+    outpam.bytes_per_sample = 1;
+    STRSCPY(outpam.tuple_type, "GRAYSCALE");
+
+    outrow = pnm_allocpamrow(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    MALLOCARRAY(slopeGrayMap, slopemax - slopemin + 1);
+
+    generateSlopeGrayMap(slopeGrayMap, dgamma);
+
+    for (row = 0; row < terrainPamP->height; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < terrainPamP->width - 1; ++col) {
+            int const slope = terrain[row][col+1][0] - terrain[row][col][0];
+            outrow[col][0] = brightnessOfSlope(slope, slopeGrayMap);
+        }
+        {
+            /* Wrap around to determine shade of pixel on right edge */
+            int const slope = 
+                terrain[row][0][0] - terrain[row][outpam.width-1][0];
+            outrow[outpam.width - 1][0] =
+                brightnessOfSlope(slope, slopeGrayMap);
+        }
+        pnm_writepamrow(&outpam, outrow);
+    }
+
+    free(slopeGrayMap);
+    pnm_freepamrow(outrow);
+}
+
+
+
+static void
+readTerrain(FILE *       const ifP,
+            struct pam * const pamP,
+            tuple ***    const tuplesP) {
+
+    *tuplesP = pnm_readpam(ifP, pamP, PAM_STRUCT_SIZE(tuple_type));
+}
+            
+            
+
+int
+main(int argc, const char ** argv) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam terrainPam;
+    tuple ** terrain;
+        /* Array of elevations */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readTerrain(ifP, &terrainPam, &terrain);
+
+    writeShadedRelief(&terrainPam, terrain, cmdline.gamma, stdout);
+    
+    return 0;
+}
+
+
diff --git a/generator/pgmcrater b/generator/pgmcrater
new file mode 100755
index 00000000..1c22ed70
--- /dev/null
+++ b/generator/pgmcrater
@@ -0,0 +1,94 @@
+#!/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
+#  Pamcrater/Pamshadedrelief.  An old program coded to call Pgmcrater will
+#  continue working because this interface exists.  All new (or newly
+#  modified) programs should call Pamcrater and Pamshadedrelief instead.
+#
+#  In days past, Pamcrater and Pamshadedrelief did not exist.  Pgmcrater did
+#  both jobs together, with PGM output.
+##############################################################################
+
+use strict;
+
+use Getopt::Long;
+
+my @pgmcraterArgv = @ARGV;
+
+my $validOptions = GetOptions(
+    'number=i'     => \my $numberOpt,
+    'height=i'     => \my $heightOpt,
+    'ysize=i'      => \my $ysizeOpt,
+    'width=i'      => \my $widthOpt,
+    'xsize=i'      => \my $xsizeOpt,
+    'gamma=i'      => \my $gammaOpt,
+    'randomseed=i' => \my $randomseedOpt);
+
+if (!$validOptions) {
+    print STDERR "Invalid syntax\n";
+    exit(100);
+}
+
+my $pamcraterArgs;
+
+$pamcraterArgs = '';  # initial value
+
+if (defined($numberOpt)) {
+    $pamcraterArgs .= "'-number=$numberOpt' ";
+}
+
+if (defined($heightOpt)) {
+    $pamcraterArgs .= "'-height=$heightOpt' ";
+} elsif (defined($ysizeOpt)) {
+    $pamcraterArgs .= "'-height=$ysizeOpt' ";
+}
+
+if (defined($widthOpt)) {
+    $pamcraterArgs .= "'-width=$widthOpt' ";
+} elsif (defined($xsizeOpt)) {
+    $pamcraterArgs .= "'-width=$xsizeOpt' ";
+}
+
+if (defined($randomseedOpt)) {
+    $pamcraterArgs .= "'-randomseed=$randomseedOpt' ";
+}
+
+my $pamshadedreliefArgs;
+
+$pamshadedreliefArgs = '';  # initial value
+
+if (defined($gammaOpt)) {
+    $pamshadedreliefArgs .= "'-gamma=$gammaOpt' ";
+}
+
+my $termStatus =
+    system(
+        "pamcrater $pamcraterArgs | " .
+        "pamshadedrelief $pamshadedreliefArgs |" .
+        "pamtopnm");
+
+exit($termStatus == 0 ? 0 : 1);
diff --git a/generator/pgmcrater.c b/generator/pgmcrater.c
deleted file mode 100644
index 25b7b817..00000000
--- a/generator/pgmcrater.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
-
-              Fractal cratering
-
-       Designed and implemented in November of 1989 by:
-
-        John Walker
-        Autodesk SA
-        Avenue des Champs-Montants 14b
-        CH-2074 MARIN
-        Switzerland
-        Usenet: kelvin@Autodesk.com
-        Fax:    038/33 88 15
-        Voice:  038/33 76 33
-
-    The  algorithm  used  to  determine crater size is as described on
-    pages 31 and 32 of:
-
-    Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal
-        Images, New York: Springer Verlag, 1988.
-
-    The  mathematical  technique  used  to calculate crater radii that
-    obey the proper area law distribution from a uniformly distributed
-    pseudorandom sequence was developed by Rudy Rucker.
-
-    Permission  to  use, copy, modify, and distribute this software and
-    its documentation  for  any  purpose  and  without  fee  is  hereby
-    granted,  without any conditions or restrictions.  This software is
-    provided "as is" without express or implied warranty.
-
-                PLUGWARE!
-
-    If you like this kind of stuff, you may also enjoy "James  Gleick's
-    Chaos--The  Software"  for  MS-DOS,  available for $59.95 from your
-    local software store or directly from Autodesk, Inc., Attn: Science
-    Series,  2320  Marinship Way, Sausalito, CA 94965, USA.  Telephone:
-    (800) 688-2344 toll-free or, outside the  U.S. (415)  332-2344  Ext
-    4886.   Fax: (415) 289-4718.  "Chaos--The Software" includes a more
-    comprehensive   fractal    forgery    generator    which    creates
-    three-dimensional  landscapes  as  well as clouds and planets, plus
-    five more modules which explore other aspects of Chaos.   The  user
-    guide  of  more  than  200  pages includes an introduction by James
-    Gleick and detailed explanations by Rudy Rucker of the  mathematics
-    and algorithms used by each program.
-
-*/
-
-/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right
- * edge. Make craters wrap around the image (enables tiling of image).
- */
-
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
-
-#include <assert.h>
-#include <math.h>
-
-#include "pm_c_util.h"
-#include "pgm.h"
-#include "mallocvar.h"
-#include "shhopt.h"
-
-
-struct CmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    unsigned int number;
-    unsigned int height;
-    unsigned int width;
-    float        gamma;
-    unsigned int randomseed;
-    unsigned int randomseedSpec;
-};
-
-
-
-static void
-parseCommandLine(int argc, const char ** const argv,
-                 struct CmdlineInfo * const cmdlineP) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optEntry * option_def;
-        /* Instructions to OptParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-    unsigned int option_def_index;
-
-    unsigned int numberSpec, heightSpec, widthSpec, gammaSpec;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "number",   OPT_UINT,    &cmdlineP->number,
-            &numberSpec,      0);
-    OPTENT3(0,   "height",   OPT_UINT,    &cmdlineP->height,
-            &heightSpec,      0);
-    OPTENT3(0,   "ysize",    OPT_UINT,    &cmdlineP->height,
-            &heightSpec,      0);
-    OPTENT3(0,   "width",    OPT_UINT,    &cmdlineP->width,
-            &widthSpec,       0);
-    OPTENT3(0,   "xsize",    OPT_UINT,    &cmdlineP->width,
-            &widthSpec,       0);
-    OPTENT3(0,   "gamma",    OPT_FLOAT,    &cmdlineP->gamma,
-            &gammaSpec,       0);
-    OPTENT3(0,   "randomseed",   OPT_UINT,    &cmdlineP->randomseed,
-            &cmdlineP->randomseedSpec,      0);
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
-
-    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-    if (argc-1 > 0)
-        pm_error("There are no non-option arguments.  You specified %u",
-                 argc-1);
-
-    if (!numberSpec)
-        cmdlineP->number = 50000;
-
-    if (cmdlineP->number == 0)
-        pm_error("-number must be positive");
-
-    if (!heightSpec)
-        cmdlineP->height = 256;
-
-    if (cmdlineP->height == 0)
-        pm_error("-height must be positive");
-
-    if (!widthSpec)
-        cmdlineP->width = 256;
-
-    if (cmdlineP->width == 0)
-        pm_error("-width must be positive");
-
-    if (!gammaSpec)
-        cmdlineP->gamma = 1.0;
-
-    if (cmdlineP->gamma <= 0.0)
-        pm_error("gamma correction must be greater than 0");
-
-    free(option_def);
-}
-
-
-
-/* Definitions for obtaining random numbers. */
-
-/*  Display parameters  */
-
-#define SCRX    screenxsize       /* Screen width */
-#define SCRY    screenysize       /* Screen height */
-#define SCRGAMMA 1.0              /* Display gamma */
-
-#define RGBQuant    255
-
-
-static double const ImageGamma = 0.5;     /* Inherent gamma of mapped image */
-
-static double const arand = 32767.0;      /* Random number parameters */
-
-static double const CdepthPower = 1.5;    /* Crater depth power factor */
-
-static double DepthBias;  /* sqrt(.5) */
-
-static int const slopemin = -52;
-static int const slopemax = 52;
-
-
-static double const
-Cast(double const low,
-     double const high) {
-
-    return low + (high - low) * ((rand() & 0x7FFF) / arand);
-}
-
-
-
-static int
-modulo(int const t,
-       int const n) {
-
-    int m;
-
-    assert(n > 0);
-
-    m = t % n;
-
-    while (m < 0) {
-        m += n;
-    }
-
-    return m;
-}
-
-
-
-#define Auxadr(x, y) &aux[modulo(y, screenysize)*screenxsize+modulo(x, screenxsize)]
-
-
-
-static void
-generateScreenImage(const unsigned short * const aux,
-                    unsigned int           const screenxsize,
-                    unsigned int           const screenysize,
-                    unsigned char *        const slopemap) {
-
-    unsigned int row;
-    gray * pixrow;
-
-    pgm_writepgminit(stdout, screenxsize, screenysize, RGBQuant, FALSE);
-    pixrow = pgm_allocrow(screenxsize);
-
-    for (row = 0; row < screenysize; ++row) {
-        unsigned int col;
-
-        for (col = 0; col < screenxsize; ++col) {
-            int j;
-            j = *Auxadr(col+1, row) - *Auxadr(col, row);
-            j = MIN(MAX(slopemin, j), slopemax);
-            pixrow[col] = slopemap[j - slopemin];
-        }
-        pgm_writepgmrow(stdout, pixrow, screenxsize, RGBQuant, FALSE);
-    }
-    pm_close(stdout);
-    pgm_freerow(pixrow);
-
-}
-
-
-
-static void
-gencraters(struct CmdlineInfo const cmdline) {
-/*----------------------------------------------------------------------------
-   Generate cratered terrain
------------------------------------------------------------------------------*/
-    unsigned int const screenxsize = cmdline.width;  /* screen X size */
-    unsigned int const screenysize = cmdline.height; /* screen Y size */
-    double       const dgamma =      cmdline.gamma;  /* display gamma */
-    unsigned int const ncraters =    cmdline.number; /* num craters to gen */
-
-    int i, j;
-    unsigned int l;
-    unsigned short * aux;
-    unsigned char * slopemap;   /* Slope to pixel map */
-
-    /* Acquire the elevation array and initialize it to mean
-       surface elevation. */
-
-    MALLOCARRAY(aux, SCRX * SCRY);
-    if (aux == NULL) 
-        pm_error("out of memory allocating elevation array");
-
-    /* Acquire the elevation buffer and initialize to mean
-       initial elevation. */
-
-    for (i = 0; i < SCRY; i++) {
-        unsigned short *zax = aux + (((long) SCRX) * i);
-
-        for (j = 0; j < SCRX; j++) {
-            *zax++ = 32767;
-        }
-    }
-
-    /* Every time we go around this loop we plop another crater
-       on the surface.  */
-
-    for (l = 0; l < ncraters; l++) {
-        double g;
-        int cx = Cast(0.0, ((double) SCRX - 1)),
-            cy = Cast(0.0, ((double) SCRY - 1)),
-            gx, gy, x, y;
-        unsigned int amptot = 0, axelev;
-        unsigned int npatch = 0;
-
-
-        /* Phase 1.  Compute the mean elevation of the impact
-           area.  We assume the impact area is a
-           fraction of the total crater size. */
-
-        /* Thanks, Rudy, for this equation  that maps the uniformly
-           distributed  numbers  from   Cast   into   an   area-law
-           distribution as observed on cratered bodies. */
-
-        g = sqrt(1 / (M_PI * (1 - Cast(0, 0.9999))));
-
-        /* If the crater is tiny, handle it specially. */
-
-        if (g < 3) {
-
-            /* Set pixel to the average of its Moore neighbourhood. */
-
-            for (y = cy - 1; y <= cy + 1; y++) {
-                for (x = cx - 1; x <= cx + 1; x++) {
-                    amptot += *Auxadr(x, y);
-                    npatch++;
-                }
-            }
-            axelev = amptot / npatch;
-
-            /* Perturb the mean elevation by a small random factor. */
-
-            x = (g >= 1) ? ((rand() >> 8) & 3) - 1 : 0;
-            *Auxadr(cx, cy) = axelev + x;
-
-            /* Jam repaint sizes to correct patch. */
-
-            gx = 1;
-            gy = 0;
-
-        } else {
-
-            /* Regular crater.  Generate an impact feature of the
-               correct size and shape. */
-
-            /* Determine mean elevation around the impact area. */
-
-            gx = MAX(2, (g / 3));
-            gy = MAX(2, g / 3);
-
-            for (y = cy - gy; y <= cy + gy; y++) {
-                for (x = cx-gx; x <= cx + gx; x++) {
-                    amptot += *Auxadr(x,y);
-                    npatch++;
-                }
-            }
-            axelev = amptot / npatch;
-
-            gy = MAX(2, g);
-            g = gy;
-            gx = MAX(2, g);
-
-            for (y = cy - gy; y <= cy + gy; y++) {
-                double dy = (cy - y) / (double) gy,
-                    dysq = dy * dy;
-
-                for (x = cx - gx; x <= cx + gx; x++) {
-                    double dx = ((cx - x) / (double) gx),
-                        cd = (dx * dx) + dysq,
-                        cd2 = cd * 2.25,
-                        tcz = DepthBias - sqrt(fabs(1 - cd2)),
-                        cz = MAX((cd2 > 1) ? 0.0 : -10, tcz),
-                        roll, iroll;
-                    unsigned short av;
-
-                    cz *= pow(g, CdepthPower);
-                    if (dy == 0 && dx == 0 && ((int) cz) == 0) {
-                        cz = cz < 0 ? -1 : 1;
-                    }
-
-#define         rollmin 0.9
-                    roll = (((1 / (1 - MIN(rollmin, cd))) /
-                             (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin;
-                    iroll = 1 - roll;
-
-                    av = (axelev + cz) * iroll + (*Auxadr(x,y) + cz) * roll;
-                    av = MAX(1000, MIN(64000, av));
-                    *Auxadr(x,y) = av;
-                }
-            }
-        }
-        if ((l % 5000) == 4999) {
-            pm_message( "%u craters generated of %u (%u%% done)",
-                        l + 1, ncraters, ((l + 1) * 100) / ncraters);
-        }
-    }
-
-    i = MAX((slopemax - slopemin) + 1, 1);
-    MALLOCARRAY(slopemap, i);
-    if (slopemap == NULL)
-        pm_error("out of memory allocating slope map");
-
-    for (i = slopemin; i <= slopemax; i++) {
-
-        /* Confused?   OK,  we're using the  left-to-right slope to
-           calculate a shade based on the sine of  the  angle  with
-           respect  to the vertical (light incident from the left).
-           Then, with one exponentiation, we account for  both  the
-           inherent   gamma   of   the   image  (ad-hoc),  and  the
-           user-specified display gamma, using the identity:
-
-           (x^y)^z = (x^(y*z))             */
-
-        slopemap[i - slopemin] = i > 0 ?
-            (128 + 127.0 *
-             pow(sin((M_PI / 2) * i / slopemax),
-                 dgamma * ImageGamma)) :
-            (128 - 127.0 *
-             pow(sin((M_PI / 2) * i / slopemin),
-                 dgamma * ImageGamma));
-    }
-
-    generateScreenImage(aux, screenxsize, screenysize, slopemap);
-
-    free((char *) slopemap);
-    free((char *) aux);
-}
-
-
-
-int
-main(int argc, const char ** argv) {
-
-    struct CmdlineInfo cmdline;
-
-    pm_proginit(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
-
-    DepthBias = sqrt(0.5);
-
-    gencraters(cmdline);
-
-    exit(0);
-}
-
-
-
diff --git a/lib/colorname.c b/lib/colorname.c
index 5c371e84..c23eb13f 100644
--- a/lib/colorname.c
+++ b/lib/colorname.c
@@ -13,7 +13,7 @@
 */
 
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 600  /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include "pm_c_util.h"
 #include <ctype.h>
@@ -87,8 +87,8 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
    Open the colorname dictionary file.  Its file name is 'fileName', unless
    'fileName' is NULL.  In that case, its file name is the value of the
    environment variable whose name is RGB_ENV (e.g. "RGBDEF").  Except
-   if that environment variable is not set, it is RGB_DB1, RGB_DB2,
-   or RGB_DB3 (e.g. "/usr/lib/X11/rgb.txt"), whichever exists.
+   if that environment variable is not set, it is the first file found,
+   if any, in the search path RGB_DB_PATH.
    
    'must_open' is a logical: we must get the file open or die.  If
    'must_open' is true and we can't open the file (e.g. it doesn't
diff --git a/lib/libpam.c b/lib/libpam.c
index f188f7d6..2d82ffef 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -14,7 +14,7 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGE_FILES  
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 600  /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <string.h>
 #include <limits.h>
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index c4f2212d..f945c0a5 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -15,7 +15,7 @@
 #define _LARGE_FILES  
 
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 600  /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <string.h>
 #include <limits.h>
diff --git a/lib/libpm.c b/lib/libpm.c
index 4d6e609a..228f42c6 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -9,7 +9,7 @@
 **************************************************************************/
 
 #define _BSD_SOURCE          /* Make sure strdup is in string.h */
-#define _XOPEN_SOURCE 600    /* Make sure ftello, fseeko are defined */
+#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
 
 #include "netpbm/pm_config.h"
 
@@ -595,61 +595,85 @@ showNetpbmHelp(const char progname[]) {
 
 
 
+static void
+parseCommonOptions(int *         const argcP,
+                   const char ** const argv,
+                   bool *        const showMessagesP,
+                   bool *        const showVersionP,
+                   bool *        const showHelpP,
+                   bool *        const plainOutputP) {
+
+    unsigned int inCursor;
+    unsigned int outCursor;
+
+    *showMessagesP = true;   /* initial assumption */
+    *showVersionP  = false;  /* initial assumption */
+    *showHelpP     = false;  /* initial assumption */
+    *plainOutputP  = false;  /* initial assumption */
+
+    for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
+            if (strcaseeq(argv[inCursor], "-quiet") ||
+                strcaseeq(argv[inCursor], "--quiet")) 
+                *showMessagesP = false;
+            else if (strcaseeq(argv[inCursor], "-version") ||
+                     strcaseeq(argv[inCursor], "--version")) 
+                *showVersionP = true;
+            else if (strcaseeq(argv[inCursor], "-help") ||
+                     strcaseeq(argv[inCursor], "--help") ||
+                     strcaseeq(argv[inCursor], "-?")) 
+                *showHelpP = true;
+            else if (strcaseeq(argv[inCursor], "-plain") ||
+                     strcaseeq(argv[inCursor], "--plain"))
+                *plainOutputP = true;
+            else
+                argv[outCursor++] = argv[inCursor];
+        }
+    *argcP = outCursor;
+}
+
+
+
 void
-pm_proginit(int * const argcP, const char * argv[]) {
+pm_proginit(int *         const argcP,
+            const char ** const argv) {
 /*----------------------------------------------------------------------------
    Do various initialization things that all programs in the Netpbm package,
    and programs that emulate such programs, should do.
 
-   This includes processing global options.
+   This includes processing global options.  We scan argv[], which has *argcP
+   elements, for common options and execute the functions for the ones we
+   find.  We remove the common options from argv[], updating *argcP
+   accordingly.
 
    This includes calling pm_init() to initialize the Netpbm libraries.
 -----------------------------------------------------------------------------*/
     const char * const progname = pm_arg0toprogname(argv[0]);
         /* points to static memory in this library */
-    int argn, i;
-    bool showmessages;
-    bool show_version;
+    bool showMessages;
+    bool plain;
+    bool justShowVersion;
         /* We're supposed to just show the version information, then exit the
            program.
         */
-    bool show_help;
+    bool justShowHelp;
         /* We're supposed to just tell user where to get help, then exit the
            program.
         */
 
     pm_init(progname, 0);
 
-    /* Check for any global args. */
-    showmessages = TRUE;
-    show_version = FALSE;
-    show_help = FALSE;
-    pm_plain_output = FALSE;
-    for (argn = i = 1; argn < *argcP; ++argn) {
-        if (pm_keymatch(argv[argn], "-quiet", 6) ||
-            pm_keymatch(argv[argn], "--quiet", 7)) 
-            showmessages = FALSE;
-        else if (pm_keymatch(argv[argn], "-version", 8) ||
-                   pm_keymatch(argv[argn], "--version", 9)) 
-            show_version = TRUE;
-        else if (pm_keymatch(argv[argn], "-help", 5) ||
-                 pm_keymatch(argv[argn], "--help", 6) ||
-                 pm_keymatch(argv[argn], "-?", 2)) 
-            show_help = TRUE;
-        else if (pm_keymatch(argv[argn], "-plain", 6) ||
-                 pm_keymatch(argv[argn], "--plain", 7))
-            pm_plain_output = TRUE;
-        else
-            argv[i++] = argv[argn];
-    }
-    *argcP=i;
+    parseCommonOptions(argcP, argv,
+                       &showMessages, &justShowVersion, &justShowHelp,
+                       &plain);
+
+    pm_plain_output = plain ? 1 : 0;
 
-    pm_setMessage((unsigned int) showmessages, NULL);
+    pm_setMessage(showMessages ? 1 : 0, NULL);
 
-    if (show_version) {
+    if (justShowVersion) {
         showVersion();
-        exit( 0 );
-    } else if (show_help) {
+        exit(0);
+    } else if (justShowHelp) {
         pm_error("Use 'man %s' for help.", progname);
         /* If we can figure out a way to distinguish Netpbm programs from 
            other programs using the Netpbm libraries, we can do better here.
@@ -661,6 +685,7 @@ pm_proginit(int * const argcP, const char * argv[]) {
 }
 
 
+
 void
 pm_setMessage(int   const newState,
               int * const oldStateP) {
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index 347bab29..3d98f5e7 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -10,7 +10,7 @@
 */
 
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 600  /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <assert.h>
 #include <stdlib.h>
diff --git a/lib/pmfileio.c b/lib/pmfileio.c
index 1263261a..b629fd23 100644
--- a/lib/pmfileio.c
+++ b/lib/pmfileio.c
@@ -10,7 +10,8 @@
        does it in other libc's).  pm_config.h defines TMPDIR as P_tmpdir
        in some environments.
     */
-#define _XOPEN_SOURCE 600    /* Make sure ftello, fseeko are defined */
+#define _BSD_SOURCE    /* Make sure strdup is defined */
+#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko, strdup are defined */
 #define _LARGEFILE_SOURCE 1  /* Make sure ftello, fseeko are defined */
 #define _LARGEFILE64_SOURCE 1 
 #define _FILE_OFFSET_BITS 64
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index bb2ba92e..1fe66a27 100644
--- a/lib/util/nstring.c
+++ b/lib/util/nstring.c
@@ -113,6 +113,8 @@
  */
 
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _BSD_SOURCE  /* Make sure strdup() is in string.h */
 #define _GNU_SOURCE
    /* Because of conditional compilation, this is GNU source only if the C
       library is GNU.
diff --git a/other/pamx/pamx.c b/other/pamx/pamx.c
index dbb8b62a..e22693ea 100644
--- a/other/pamx/pamx.c
+++ b/other/pamx/pamx.c
@@ -3,6 +3,7 @@
    Copyright information is in the file COPYRIGHT
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <signal.h>
 #include <unistd.h>
diff --git a/other/pamx/window.c b/other/pamx/window.c
index 3cded1b3..e2de1577 100644
--- a/other/pamx/window.c
+++ b/other/pamx/window.c
@@ -6,6 +6,7 @@
    See COPYRIGHT file for copyright information.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 
 #include <assert.h>
diff --git a/test/Test-Order b/test/Test-Order
index 9a9210e4..cc79570e 100644
--- a/test/Test-Order
+++ b/test/Test-Order
@@ -16,12 +16,12 @@ pgmramp.test
 ppmgauss.test
 ppmcie.test
 ppmwheel.test
+pamcrater.test
 
 # Generators with random components
 
 pgmnoise.test
 ppmpat.test
-pgmcrater.test
 ppmforge.test
 ppmrough.test
 
@@ -66,6 +66,7 @@ ppmrelief.test
 pamedge.test
 ppmdim.test
 pnmshear.test
+pgmbentley.test
 
 ppmmix.test
 
@@ -99,9 +100,11 @@ pamslice-roundtrip.test
 
 # Round-trip tests : lossless converters
 
+atari-roundtrip.test
 atk-roundtrip.test
 avs-roundtrip.test
 bmp-roundtrip.test
+cis-roundtrip.test
 cmuw-roundtrip.test
 facesaver-roundtrip.test
 fits-roundtrip.test
@@ -112,12 +115,17 @@ gif-quant-roundtrip.test
 hdiff-roundtrip.test
 jbig-roundtrip.test
 leaf-roundtrip.test
+mda-roundtrip.test
 mgr-roundtrip.test
 mrf-roundtrip.test
+pcx-roundtrip.test
 pfm-roundtrip.test
+pi3-roundtrip.test
+pict-roundtrip.test
 png-roundtrip.test
 ps-roundtrip.test
 ps-alt-roundtrip.test
+sgi-roundtrip.test
 sunrast-roundtrip.test
 targa-roundtrip.test
 tiff-roundtrip.test
diff --git a/test/all-in-place.ok b/test/all-in-place.ok
index ca8ad8c3..d37fd561 100644
--- a/test/all-in-place.ok
+++ b/test/all-in-place.ok
@@ -161,7 +161,7 @@ pdbimgtopam: ok
 pfmtopam: ok
 pgmabel: ok
 pgmbentley: ok
-pgmcrater: ok
+pamcrater: ok
 pgmdeshadow: ok
 pgmedge: ok
 pgmenhance: ok
diff --git a/test/all-in-place.test b/test/all-in-place.test
index 87ddfad8..d6ccdb3c 100755
--- a/test/all-in-place.test
+++ b/test/all-in-place.test
@@ -200,7 +200,7 @@ ordinary_testprogs="\
   pfmtopam \
   pgmabel \
   pgmbentley \
-  pgmcrater \
+  pamcrater \
   pgmdeshadow \
   pgmedge \
   pgmenhance \
@@ -408,7 +408,10 @@ ${PBM_TESTPREFIX}pnmmargin --help 2> /dev/null
 # pnmtoplainpnm, ppmquantall, ppmrainbow, ppmshadow, ppmtomap
 # with trivial input.
 
-cat > ${tmpdir}/test.pbm <<EOF
+tmpdir=${tmpdir:-/tmp}
+test_pbm=${tmpdir}/test.pbm
+
+cat > ${test_pbm} <<EOF
 P1
 1 1
 1
@@ -416,17 +419,17 @@ EOF
 
 for i in anytopnm pbmtox10bm pnmnoraw pnmtoplainpnm ppmtomap ppmshadow
   do
-    ${PBM_TESTPREFIX}$i ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+    ${PBM_TESTPREFIX}$i ${test_pbm} > /dev/null 2> /dev/null;
     testExitStatus $i 0 $?
   done
 
 for i in pnmquant pnmquantall ppmquant ppmquantall
   do
-    ${PBM_TESTPREFIX}$i 2 ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+    ${PBM_TESTPREFIX}$i 2 ${test_pbm} > /dev/null 2> /dev/null;
     testExitStatus $i 0 $?
   done
 
-rm ${tmpdir}/test.pbm
+rm ${test_pbm}
 
 ${PBM_TESTPREFIX}ppmrainbow rgb:00/00/00 rgb:ff/ff/ff \
     -tmpdir=${tmpdir} -width=2 -height=2 > /dev/null
diff --git a/test/atari-roundtrip.ok b/test/atari-roundtrip.ok
new file mode 100644
index 00000000..48b8a30a
--- /dev/null
+++ b/test/atari-roundtrip.ok
@@ -0,0 +1,3 @@
+3583332118 192013
+3583332118 192013
+3583332118 192013
diff --git a/test/atari-roundtrip.test b/test/atari-roundtrip.test
new file mode 100755
index 00000000..85929a8b
--- /dev/null
+++ b/test/atari-roundtrip.test
@@ -0,0 +1,40 @@
+# This script tests: ppmtopi1 pi1toppm ppmtoneo neotoppm
+# Also requires: pgmramp pamscale pbmmake rgb3toppm
+
+  alias ppmtopi1="${PBM_TESTPREFIX}ppmtopi1"
+  alias pi1toppm="${PBM_TESTPREFIX}pi1toppm"
+  alias ppmtoneo="${PBM_TESTPREFIX}ppmtoneo"
+  alias neotoppm="${PBM_TESTPREFIX}neotoppm"
+  alias pgmramp="${PBM_BINPREFIX}pgmramp"
+  alias pamscale="${PBM_BINPREFIX}pamscale"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias rgb3toppm="${PBM_BINPREFIX}rgb3toppm"
+  shopt -s expand_aliases
+
+# Atari pi1 and neo files have the following characteristics:
+# Size: 320x200
+# Maxval: 7
+# Color palette: at most 16 entries
+
+tmpdir=${tmpdir:-/tmp}
+t_red=${tmpdir}/t.red
+t_grn=${tmpdir}/t.grn
+t_blu=${tmpdir}/t.blu
+t_ppm=${tmpdir}/t.ppm
+
+pgmramp -lr -maxval=3 4 1 | \
+  pamscale -xscale=80 -yscale=200 -nomix  > ${t_red}
+pbmmake -g 16 10 | pamenlarge 20  > ${t_grn}
+pbmmake -g  8  5 | pamenlarge 40  > ${t_blu}
+rgb3toppm ${t_red} ${t_grn} ${t_blu} | pamdepth 7 > ${t_ppm}
+
+#Test 0.  Should print: 3583332118 192013
+cksum < ${t_ppm}
+
+#Test 1.  Should print: 3583332118 192013
+ppmtopi1 ${t_ppm} | pi1toppm | cksum
+
+#Test 2.  Should print: 3583332118 192013
+ppmtoneo ${t_ppm} | neotoppm | cksum
+
+rm ${t_red} ${t_grn} ${t_blu} ${t_ppm}
diff --git a/test/cis-roundtrip.ok b/test/cis-roundtrip.ok
new file mode 100644
index 00000000..da90078b
--- /dev/null
+++ b/test/cis-roundtrip.ok
@@ -0,0 +1,2 @@
+2631579683 1546
+2425386270 41
diff --git a/test/cis-roundtrip.test b/test/cis-roundtrip.test
new file mode 100755
index 00000000..895fe60e
--- /dev/null
+++ b/test/cis-roundtrip.test
@@ -0,0 +1,20 @@
+#! /bin/bash
+# This script tests: pbmtocis cistopbm
+# Also requires: pbmmake pamenlarge pamcut
+
+  alias pbmtocis="${PBM_TESTPREFIX}pbmtocis"
+  alias cistopbm="${PBM_TESTPREFIX}cistopbm"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pamenlarge="${PBM_BINPREFIX}pamenlarge"
+  alias pamcut="${PBM_BINPREFIX}pamcut"
+  shopt -s expand_aliases
+
+# Output images produced by pbmtocis are of fixed size,
+# either 128x96 or 256x192.
+# Smaller input images are padded, larger ones are cropped.
+
+# Test 1. Should print 2631579683 1546
+pbmmake -g 128 96 | pbmtocis | cistopbm | cksum
+
+# Test 2. Should print 2425386270 41
+pbmtocis testgrid.pbm | cistopbm | pamcut 0 0 14 16 | cksum
diff --git a/test/facesaver-roundtrip.ok b/test/facesaver-roundtrip.ok
index 32a4743b..430f3f5f 100644
--- a/test/facesaver-roundtrip.ok
+++ b/test/facesaver-roundtrip.ok
@@ -1 +1 @@
-2871603838 33838
+1571496937 33838
diff --git a/test/facesaver-roundtrip.test b/test/facesaver-roundtrip.test
index 362edaf5..76de380d 100755
--- a/test/facesaver-roundtrip.test
+++ b/test/facesaver-roundtrip.test
@@ -1,13 +1,12 @@
 #! /bin/bash
 # This script tests: pgmtofs fstopgm
-# Also requires: ppmtopgm
+# Also requires: pamchannel
 
   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
+# Should produce 1571496937 33838, cksum of testimg.red
 
-ppmtopgm testimg.ppm | \
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
   pgmtofs | fstopgm | cksum
diff --git a/test/gif-quant-roundtrip.test b/test/gif-quant-roundtrip.test
index b5f69edd..7f99ca6e 100755
--- a/test/gif-quant-roundtrip.test
+++ b/test/gif-quant-roundtrip.test
@@ -11,10 +11,13 @@
 
 colors=15      # any value between 2 - 256 works
 
-pnmquant $colors testimg.ppm > ${tmpdir}/quant.ppm &&
-pamtogif ${tmpdir}/quant.ppm | giftopnm | \
-   cmp -s - ${tmpdir}/quant.ppm > /dev/null
+tmpdir=${tmpdir:-/tmp}
+quant_ppm=${tmpdir}/quant.ppm
+
+pnmquant ${colors} testimg.ppm > ${quant_ppm} &&
+pamtogif ${quant_ppm} | giftopnm | \
+   cmp -s - ${quant_ppm} > /dev/null
 echo $?
 
-rm ${tmpdir}/quant.ppm
+rm ${quant_ppm}
 
diff --git a/test/gif-roundtrip.ok b/test/gif-roundtrip.ok
index ece7593c..552bf90c 100644
--- a/test/gif-roundtrip.ok
+++ b/test/gif-roundtrip.ok
@@ -1,8 +1,8 @@
-2871603838 33838
-2871603838 33838
-2871603838 33838
-2871603838 33838
 1926073387 101484
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
 2425386270 41
 2425386270 41
 2425386270 41
diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test
index 574638ac..cb62bfb1 100755
--- a/test/gif-roundtrip.test
+++ b/test/gif-roundtrip.test
@@ -1,47 +1,59 @@
 #! /bin/bash
 # This script tests: giftopnm pamtogif
-# Also requires: ppmtopgm ppmtorgb3 rgb3toppm pbmmake
+# Also requires: 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
-
-rm  ${tmpdir}/testimg.pgm
-rm  ${tmpdir}/testimg.gif
+tmpdir=${tmpdir:-/tmp}
 
-# Test 2. Break up input image into three monochrome planes,
+# Test 1. Break up input image into three monochrome planes,
 # maxval 255.  Transform each plane to gif and back to pgm.
 # Reassemble the planes.  Result should be identical to input.
 # Should print 1926073387 101484
 
+test_ppm=${tmpdir}/testimg.ppm
+
 cp testimg.ppm ${tmpdir} &&
-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 - | \
+ppmtorgb3 ${test_ppm} &&
+
+test_red=${tmpdir}/testimg.red
+test_grn=${tmpdir}/testimg.grn
+test_blu=${tmpdir}/testimg.blu
+out_red=${tmpdir}/out.red
+out_grn=${tmpdir}/out.grn
+out_blu=${tmpdir}/out.blu
+
+pamtogif ${test_red} | giftopnm > ${out_red} &&
+pamtogif ${test_grn} | giftopnm > ${out_grn} &&
+pamtogif ${test_blu} | giftopnm | \
+  rgb3toppm ${out_red} ${out_grn} - | \
   cksum
 
-rm ${tmpdir}/testimg.{ppm,red,grn,blu} ${tmpdir}/out.{red,grn}
+rm ${test_ppm} ${test_grn} ${test_blu} \
+   ${out_red} ${out_grn} ${out_blu}
+
+
+# Test 2. Should produce 1571496937 33838
+# which is the result of cksum testimg.red
+# four times
+
+test_gif=${tmpdir}/testimg.gif
+
+pamtogif ${test_red} | giftopnm | cksum
+pamtogif -interlace ${test_red} | giftopnm | cksum
+pamtogif -sort ${test_red} | tee ${test_gif} | \
+  giftopnm | cksum
+echo "junk" >> ${test_gif} && \
+  giftopnm -image=1 -quitearly ${test_gif} | cksum
+
+rm  ${test_gif} ${test_red}
+
 
 # Test 3. Should produce 2425386270 41 five times.
 
diff --git a/test/ilbm-roundtrip.ok b/test/ilbm-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/ilbm-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/ilbm-roundtrip.test b/test/ilbm-roundtrip.test
new file mode 100755
index 00000000..495daf53
--- /dev/null
+++ b/test/ilbm-roundtrip.test
@@ -0,0 +1,20 @@
+#! /bin/bash
+# This script tests: ppmtoilbm ilbmtoppm
+# Also requires: pamseq pamdepth pamtopnm pnmremap
+
+  alias ppmtoilbm="${PBM_TESTPREFIX}ppmtoilbm"
+  alias ilbmtoppm="${PBM_TESTPREFIX}ilbmtoppm"
+  alias pamseq="${PBM_BINPREFIX}pamseq"
+  alias pamdepth="${PBM_BINPREFIX}pamdepth"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  alias pnmremap="${PBM_BINPREFIX}pnmremap"
+  shopt -s expand_aliases
+
+#Test.  Should produce 1926073387 101484
+ppmtoilbm testimg.ppm | ilbmtoppm | cksum
+
+#Test.  Should print 984199586 101484
+pamseq 3 5 -tupletype=RGB | pamdepth 255 | pamtopnm | \
+  pnmremap -mapfile=- testimg.ppm | ppmtoilbm | ilbmtoppm | cksum
+
+
diff --git a/test/jbig-roundtrip.ok b/test/jbig-roundtrip.ok
index a951db19..b98a694b 100644
--- a/test/jbig-roundtrip.ok
+++ b/test/jbig-roundtrip.ok
@@ -1,2 +1,2 @@
 2425386270 41
-2871603838 33838
+1571496937 33838
diff --git a/test/jbig-roundtrip.test b/test/jbig-roundtrip.test
index 766148c6..9ce23f51 100755
--- a/test/jbig-roundtrip.test
+++ b/test/jbig-roundtrip.test
@@ -1,14 +1,14 @@
 #! /bin/bash
 # This script tests: pnmtojbig jbigtopnm
-# Also requires: ppmtopgm
+# Also requires: pamchannel
 
   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
+# Test 2.  Should print 1571496937 33838
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnmtojbig | jbigtopnm | cksum
diff --git a/test/macp-roundtrip.ok b/test/macp-roundtrip.ok
new file mode 100644
index 00000000..20e24348
--- /dev/null
+++ b/test/macp-roundtrip.ok
@@ -0,0 +1,2 @@
+2425386270 41
+1005016577 51851
diff --git a/test/macp-roundtrip.test b/test/macp-roundtrip.test
new file mode 100755
index 00000000..39228e5a
--- /dev/null
+++ b/test/macp-roundtrip.test
@@ -0,0 +1,15 @@
+#! /bin/bash
+# This script tests: pbmtomacp macptopbm
+# Also requires: pamcrop pbmmake
+
+  alias pbmtomacp="${PBM_TESTPREFIX}pbmtomacp"
+  alias macptopbm="${PBM_TESTPREFIX}macptopbm"
+  alias pnmcrop="${PBM_BINPREFIX}pnmcrop"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  shopt -s expand_aliases
+
+#Test 1. Should produce 2425386270 41
+pbmtomacp testgrid.pbm | macptopbm | pnmcrop | cksum
+
+#Test 2. Should produce 1005016577 51851
+pbmmake -g 576 720 | pbmtomacp | macptopbm | cksum
diff --git a/test/mda-roundtrip.ok b/test/mda-roundtrip.ok
new file mode 100644
index 00000000..ef27ffd0
--- /dev/null
+++ b/test/mda-roundtrip.ok
@@ -0,0 +1,2 @@
+1757803444 169
+2425386270 41
diff --git a/test/mda-roundtrip.test b/test/mda-roundtrip.test
new file mode 100755
index 00000000..3bde61ee
--- /dev/null
+++ b/test/mda-roundtrip.test
@@ -0,0 +1,19 @@
+#! /bin/bash
+# This script tests: pbmtomda mdatopbm
+# Also requires: pbmmake pnmpad pamcut
+
+  alias pbmtomda="${PBM_TESTPREFIX}pbmtomda"
+  alias mdatopbm="${PBM_TESTPREFIX}mdatopbm"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pnmpad="${PBM_BINPREFIX}pnmpad"
+  alias pamcut="${PBM_BINPREFIX}pamcut"
+  shopt -s expand_aliases
+
+# Pbmtomda requires input width and height to be multiples of 8. 
+
+# Test 1.  Should print 1757803444 169
+pbmmake -g 32 40 | pbmtomda | mdatopbm | cksum
+
+# Test 2.  Should print 2425386270 41
+pnmpad -right 2 testgrid.pbm | \
+  pbmtomda | mdatopbm | pamcut 0 0 14 16 | cksum
diff --git a/test/pamchannel.test b/test/pamchannel.test
index e3da552c..9ac71f13 100755
--- a/test/pamchannel.test
+++ b/test/pamchannel.test
@@ -6,7 +6,6 @@
   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.
 
@@ -26,8 +25,8 @@ pamchannel -infile testimg.ppm 0 | \
 # Test 2. green channel
 # Should produce  394856971 33838
 
-pamchannel -infile testimg.ppm 1 | \
-  pamtopnm --assume | cksum
+pamchannel -infile testimg.ppm -tupletype="GRAYSCALE" 1 | \
+  pamtopnm | cksum
 
 # Test 3. blue channel
 # Should produce 3164158573 33838
diff --git a/test/pamcrater.ok b/test/pamcrater.ok
new file mode 100644
index 00000000..cfb186a6
--- /dev/null
+++ b/test/pamcrater.ok
@@ -0,0 +1,6 @@
+4
+4
+4
+2
+2
+2
diff --git a/test/pamcrater.test b/test/pamcrater.test
new file mode 100755
index 00000000..1d182208
--- /dev/null
+++ b/test/pamcrater.test
@@ -0,0 +1,53 @@
+#! /bin/bash
+# This script tests: pamcrater pamshadedrelief
+# Also requires: pamslice pamvalidate
+
+  alias pamcrater="${PBM_TESTPREFIX}pamcrater"
+  alias pamslice="${PBM_TESTPREFIX}pamshadedrelief"
+  alias pamslice="${PBM_BINPREFIX}pamslice"
+  alias pamvalidate="${PBM_TESTPREFIX}pamvalidate"
+  shopt -s expand_aliases
+
+tmpdir=${tmpdir:-/tmp}
+
+test_pam=${tmpdir}/test.pam
+testshaded_pam=${tmpdir}/testshaded_pam
+test10_pam=${tmpdir}/test10.pam
+test50_pam=${tmpdir}/test50.pam
+test100_pam=${tmpdir}/test100.pam
+test150_pam=${tmpdir}/test150.pam
+
+# Test 1.  Should print 4 three times
+
+pamcrater --test --radius=10 > $test10_pam
+pamcrater --test --radius=50 > $test50_pam
+pamcrater --test --radius=100 > $test100_pam
+pamcrater --test --radius=150 > $test150_pam
+
+pamstack ${test10_pam} ${test50_pam} ${test100_pam} ${test150_pam} |
+  pamvalidate > ${test_pam} 
+
+for i in 1 10 70
+  do
+    ( pamslice -row=$((128 + $i))  ${test_pam} | cksum &&
+      pamslice -row=$((128 - $i))  ${test_pam} | cksum &&
+      pamslice -col=$((128 + $i))  ${test_pam} | cksum &&
+      pamslice -col=$((128 - $i))  ${test_pam} | cksum
+    ) | uniq -c | awk '{print $1}'
+  done
+
+rm ${test_pam} ${test10_pam} ${test50_pam}
+
+# Test 2.  Should print 2 three times
+
+pamshadedrelief ${test100_pam} > ${testshaded_pam}
+
+( pamslice -row=$((128 + 12))  ${testshaded_pam} | cksum &&
+  pamslice -row=$((128 - 12))  ${testshaded_pam} | cksum &&
+  pamslice -row=$((128 + 31))  ${testshaded_pam} | cksum &&
+  pamslice -row=$((128 - 31))  ${testshaded_pam} | cksum &&
+  pamslice -row=$((128 + 99))  ${testshaded_pam} | cksum &&
+  pamslice -row=$((128 - 99))  ${testshaded_pam} | cksum
+) | uniq -c | awk '{print $1}'
+
+rm ${testshaded_pam} ${test100_pam}
diff --git a/test/pamdice-roundtrip.test b/test/pamdice-roundtrip.test
index 7319249a..7b863bca 100755
--- a/test/pamdice-roundtrip.test
+++ b/test/pamdice-roundtrip.test
@@ -6,8 +6,11 @@
   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
+tmpdir=${tmpdir:-/tmp}
+fname_stem=${tmpdir}/a
 
-rm ${tmpdir}/a_?_?.ppm
+pamdice testimg.ppm -outstem=${fname_stem} -width=50 -height=40
+pamundice ${fname_stem}_%1d_%1a.ppm -down=4 -across=5 | cksum
+
+rm ${fname_stem}_?_?.ppm
 
diff --git a/test/pamditherbw.ok b/test/pamditherbw.ok
index 0711fa38..e8186c24 100644
--- a/test/pamditherbw.ok
+++ b/test/pamditherbw.ok
@@ -1,4 +1,4 @@
-1421025574 33894
-164421928 33894
-486487763 33894
-3606822102 33894
+1316122660 33894
+3342429190 33894
+3325147568 33894
+4124728025 33894
diff --git a/test/pamditherbw.test b/test/pamditherbw.test
index fef71efa..1ac1391c 100755
--- a/test/pamditherbw.test
+++ b/test/pamditherbw.test
@@ -1,35 +1,34 @@
 #! /bin/bash
 # This script tests: pamditherbw
-# Also requires: ppmtopgm
+# Also requires: pamchannel
 
   alias pamditherbw="${PBM_TESTPREFIX}pamditherbw"
-  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
   shopt -s expand_aliases
 
-# Make test input
-ppmtopgm testimg.ppm >${tmpdir}/testimg.pgm
+tmpdir=${tmpdir:-/tmp}
+test_red=${tmpdir}/testimg.red
 
 # Test 1.  Simple threshold
-pamditherbw -threshold -val=0.5 \
-   ${tmpdir}/testimg.pgm | cksum
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  tee ${test_red} | \
+  pamditherbw -threshold -val=0.5 | cksum
 
 # Test 2.  Floyd-Steinberg
-#pamditherbw -floyd -val=0.5 ${tmpdir}/testimg.pgm | cksum
+#pamditherbw -floyd -val=0.5 ${test_red} | cksum
 
 # Test 3. Atkinson
-#pamditherbw -atkinson -val=0.5 ${tmpdir}/testimg.pgm | cksum
+#pamditherbw -atkinson -val=0.5 ${test_red} | cksum
 
 # Test 4. Hilbert
-pamditherbw -hilbert ${tmpdir}/testimg.pgm | cksum
+pamditherbw -hilbert ${test_red} | cksum
 
 # Test 5. Dither-8
-pamditherbw -dither8 ${tmpdir}/testimg.pgm | cksum
+pamditherbw -dither8 ${test_red} | cksum
 
 # Test 6. Cluster4
-pamditherbw -cluster4 ${tmpdir}/testimg.pgm | cksum
+pamditherbw -cluster4 ${test_red} | cksum
 
 # Test 7. Atkinson
-#pamditherbw -atkinson -val=0.5 ${tmpdir}/testimg.pgm | cksum
+#pamditherbw -atkinson -val=0.5 ${test_red} | cksum
 
-# Remove test file
-rm ${tmpdir}/testimg.pgm
+rm ${test_red}
diff --git a/test/pamenlarge.ok b/test/pamenlarge.ok
index 40be5918..055262c9 100644
--- a/test/pamenlarge.ok
+++ b/test/pamenlarge.ok
@@ -1,4 +1,4 @@
 3424505894 913236
-4152147096 304422
+3763267672 304422
 3342398172 297
 237488670 3133413
diff --git a/test/pamenlarge.test b/test/pamenlarge.test
index c1958d14..8f9e3c70 100755
--- a/test/pamenlarge.test
+++ b/test/pamenlarge.test
@@ -1,15 +1,15 @@
 #! /bin/bash
 # This script tests: pamenlarge
-# Also requires: ppmtopgm
+# Also requires: pamchannel
 
   alias pamenlarge="${PBM_TESTPREFIX}pamenlarge"
-  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
   shopt -s expand_aliases
 
 # Test 1.  Should print 3424505894 913236
 pamenlarge 3 testimg.ppm | cksum
-# Test 2.  Should print 4152147096 304422
-ppmtopgm testimg.ppm | pamenlarge 3 | cksum
+# Test 2.  Should print 3763267672 304422
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pamenlarge 3 | cksum
 # Test 3.  Should print 3342398172 297
 pamenlarge 3 testgrid.pbm | cksum
 # Test 4.  Should print 237488670 3133413
diff --git a/test/pamfile.ok b/test/pamfile.ok
index 6cdf0433..57cc8cfd 100644
--- a/test/pamfile.ok
+++ b/test/pamfile.ok
@@ -1,3 +1,5 @@
 testimg.ppm:	PPM raw, 227 by 149  maxval 255
 testgrid.pbm:	PBM raw, 14 by 16
 stdin:	PGM raw, 227 by 149  maxval 255
+stdin:	PAM, 227 by 149 by 1 maxval 255
+    Tuple type: GRAYSCALE
diff --git a/test/pamfile.test b/test/pamfile.test
index 689578f1..1c1d3d23 100755
--- a/test/pamfile.test
+++ b/test/pamfile.test
@@ -1,11 +1,14 @@
 #! /bin/bash
 # This script tests: pamfile
-# Also requires: ppmtopgm
+# Also requires: pamchannel
 
   alias pamfile="${PBM_TESTPREFIX}pamfile"
-  alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
   shopt -s expand_aliases
 
 pamfile testimg.ppm
 pamfile testgrid.pbm
-ppmtopgm testimg.ppm | pamfile
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | pamfile
+pamchannel -tupletype="GRAYSCALE" -infile=testimg.ppm 0 | pamfile
+
+
+
diff --git a/test/pamflip1.test b/test/pamflip1.test
index 0f60b72b..0f60b72b 100644..100755
--- a/test/pamflip1.test
+++ b/test/pamflip1.test
diff --git a/test/pamflip2.test b/test/pamflip2.test
index d9418b25..d9418b25 100644..100755
--- a/test/pamflip2.test
+++ b/test/pamflip2.test
diff --git a/test/pamslice-roundtrip.test b/test/pamslice-roundtrip.test
index edec0d26..f17765d1 100755
--- a/test/pamslice-roundtrip.test
+++ b/test/pamslice-roundtrip.test
@@ -30,14 +30,18 @@
 # Add header and reconstruct ppm image.
 # Should print 914327477 4864
 
-pamcut 50 50 49 33 testimg.ppm > ${tmpdir}/test4933.ppm
+tmpdir=${tmpdir:-/tmp}
+
+test4933_ppm=${tmpdir}/test4933.ppm
+
+pamcut 50 50 49 33 testimg.ppm > ${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}';
+     pamslice -row=$i ${test4933_ppm} | awk '{print $2, $3, $4}';
      done ) | pnmtopnm | cksum
 
 # Same as above test 2, but take cols instead of rows.
@@ -48,25 +52,27 @@ pamcut 50 50 49 33 testimg.ppm > ${tmpdir}/test4933.ppm
  echo "255"
  seq 0 48 | while read i;
      do
-     pamslice -col=$i ${tmpdir}/test4933.ppm | awk '{print $2, $3, $4}';
+     pamslice -col=$i ${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
+testeven_ppm=${tmpdir}/testeven.ppm
+testodd_ppm=${tmpdir}/testodd.ppm
+
+pamdeinterlace -takeodd ${test4933_ppm} > ${testodd_ppm}
+pamdeinterlace -takeeven ${test4933_ppm} > ${testeven_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}';
+     pamslice -row=$i ${testeven_ppm} | awk '{print $2, $3, $4}';
+     pamslice -row=$i ${testodd_ppm} | awk '{print $2, $3, $4}';
      done
-     pamslice -row=16 ${tmpdir}/testevn.ppm | awk '{print $2, $3, $4}';
+     pamslice -row=16 ${testeven_ppm} | awk '{print $2, $3, $4}';
   ) | pnmtopnm | tee /tmp/z | cksum
 
-rm ${tmpdir}/test4933.ppm ${tmpdir}/testodd.ppm ${tmpdir}/testevn.ppm
-
+rm ${test4933_ppm} ${testodd_ppm} ${testeven_ppm}
diff --git a/test/pbmclean.test b/test/pbmclean.test
index 28242d30..5f324c5e 100755
--- a/test/pbmclean.test
+++ b/test/pbmclean.test
@@ -8,15 +8,17 @@
   alias pnmmargin="${PBM_BINPREFIX}pnmmargin"
   shopt -s expand_aliases
 
-pbmmake -g 3 3 | pnmmargin -black 2 \
- >${tmpdir}/test.pbm
+tmpdir=${tmpdir:-/tmp}
+test_pbm=${tmpdir}/test.pbm
+
+pbmmake -g 3 3 | pnmmargin -black 2 > ${test_pbm}
 
 for n in 1 2 3 4 5 6 7 8
 do
-pbmclean -min=$n -black -plain ${tmpdir}/test.pbm
+pbmclean -min=$n -black -plain ${test_pbm}
 done
 
-rm ${tmpdir}/test.pbm
+rm ${test_pbm}
 
 # Should print 760076056 4210813
 pbmpage 1 | pbmclean -black | cksum
diff --git a/test/pcx-roundtrip.ok b/test/pcx-roundtrip.ok
new file mode 100644
index 00000000..968f46b9
--- /dev/null
+++ b/test/pcx-roundtrip.ok
@@ -0,0 +1,5 @@
+1926073387 101484
+369063776 101484
+369063776 101484
+369063776 101484
+829921912 685
diff --git a/test/pcx-roundtrip.test b/test/pcx-roundtrip.test
new file mode 100755
index 00000000..5efbbca9
--- /dev/null
+++ b/test/pcx-roundtrip.test
@@ -0,0 +1,49 @@
+# This script tests: ppmtopcx pcxtoppm
+# Also requires: pnmremap
+
+  alias ppmtopcx="${PBM_TESTPREFIX}ppmtopcx"
+  alias pcxtoppm="${PBM_TESTPREFIX}pcxtoppm"
+  alias ppmremap="${PBM_BINPREFIX}ppmremap"
+  shopt -s expand_aliases
+
+tmpdir=${tmpdir:-/tmp}
+pcxstd_ppm=${tmpdir}/pcxstd_ppm
+testpcx_ppm=${tmpdir}/test-pcx.ppm
+
+cat > ${pcxstd_ppm} << EOF
+P3
+16 1
+255
+  0   0   0
+  0   0 170
+  0 170   0
+  0 170 170
+170   0   0
+170   0 170
+170 170   0
+170 170 170
+ 85  85  85
+ 85  85 255
+ 85 255  85
+ 85 255 255
+255  85  85
+255  85 255
+255 255  85
+255 255 255
+EOF
+
+# Test 1. Should print 1926073387 101484
+ppmtopcx testimg.ppm | pcxtoppm | cksum
+
+# Test 2.  Should print 369063776 101484 three times
+pnmremap testimg.ppm -mapfile=${pcxstd_ppm} | tee ${testpcx_ppm} | cksum
+ppmtopcx -stdpalette -packed ${testpcx_ppm} | pcxtoppm | cksum
+ppmtopcx -stdpalette -packed -8bit ${testpcx_ppm} | pcxtoppm | cksum
+
+rm ${testpcx_ppm} ${pcxstd_ppm}
+
+# Test 3. Should print 829921912 685 which is the
+# result of:
+# pgmtoppm < testgrid.pbm | cksum
+ppmtopcx -stdpalette -packed testgrid.pbm | pcxtoppm | cksum
+
diff --git a/test/pgmbentley.ok b/test/pgmbentley.ok
new file mode 100644
index 00000000..2ae59958
--- /dev/null
+++ b/test/pgmbentley.ok
@@ -0,0 +1 @@
+4127286153 16399
diff --git a/test/pgmbentley.test b/test/pgmbentley.test
new file mode 100755
index 00000000..8af2c59c
--- /dev/null
+++ b/test/pgmbentley.test
@@ -0,0 +1,10 @@
+#! /bin/bash
+# This script tests: pgmbentley
+# Also requires: pgmramp
+
+  alias pgmbentley="${PBM_TESTPREFIX}pgmbentley"
+  alias pgmramp="${PBM_BINPREFIX}pgmramp"
+  shopt -s expand_aliases
+
+# Test. Should produce 4127286153 16399
+pgmramp -rect 128 128 | pgmbentley | cksum
diff --git a/test/pgmtoppm.test b/test/pgmtoppm.test
index cdd53623..f2d0fffb 100755
--- a/test/pgmtoppm.test
+++ b/test/pgmtoppm.test
@@ -8,17 +8,22 @@
   alias pgmramp="${PBM_BINPREFIX}pgmramp"
   shopt -s expand_aliases
 
-pgmramp -maxval=5 -lr 256 1 >${tmpdir}/test.pgm
+tmpdir=${tmpdir:-/tmp}
+
+test_pgm=${tmpdir}/test.pgm
+palette=${tmpdir}/palette
+
+pgmramp -maxval=5 -lr 256 1 >${test_pgm}
 pamseq 3 5 -tupletype=RGB | pamtopnm \
-  >${tmpdir}/palette
+  >${palette}
 
 # Test 1.
-pgmtoppm green ${tmpdir}/test.pgm | cksum
+pgmtoppm green ${test_pgm} | cksum
 
-pgmtoppm yellow-blue ${tmpdir}/test.pgm | cksum
+pgmtoppm yellow-blue ${test_pgm} | cksum
 
-pgmtoppm -map=${tmpdir}/palette ${tmpdir}/test.pgm | cksum
+pgmtoppm -map=${palette} ${test_pgm} | cksum
 
-rm ${tmpdir}/test.pgm ${tmpdir}/palette
+rm ${test_pgm} ${palette}
 
 
diff --git a/test/pi3-roundtrip.ok b/test/pi3-roundtrip.ok
new file mode 100644
index 00000000..3fde31b1
--- /dev/null
+++ b/test/pi3-roundtrip.ok
@@ -0,0 +1,2 @@
+3139449799 32011
+2425386270 41
diff --git a/test/pi3-roundtrip.test b/test/pi3-roundtrip.test
new file mode 100755
index 00000000..cd4232eb
--- /dev/null
+++ b/test/pi3-roundtrip.test
@@ -0,0 +1,22 @@
+# This script tests: pbmtopi3 pi3topbm
+# Also requires: pbmmake pamcut
+
+  alias pbmtopi3="${PBM_TESTPREFIX}pbmtopi3"
+  alias pi3topbm="${PBM_TESTPREFIX}pi3topbm"
+  alias pbmmake="${PBM_BINPREFIX}pbmmake"
+  alias pamcut="${PBM_BINPREFIX}pamcut"
+  shopt -s expand_aliases
+
+# The pi3 image format specifies a fixed image size of 640x400.
+# Pbmtopi3 rejects images that do not conform.
+
+# Test
+# Should print: 3139449799 32011
+pbmmake -g 640 400 | pbmtopi3 | pi3topbm | cksum
+
+# Test 2. 
+# Should print: 2425386270 41
+pamcut -pad 0 0 640 400 testgrid.pbm | \
+  pbmtopi3 | pi3topbm | pamcut 0 0 14 16 | cksum
+
+
diff --git a/test/pict-roundtrip.ok b/test/pict-roundtrip.ok
new file mode 100644
index 00000000..d50b54bc
--- /dev/null
+++ b/test/pict-roundtrip.ok
@@ -0,0 +1 @@
+984199586 101484
diff --git a/test/pict-roundtrip.test b/test/pict-roundtrip.test
new file mode 100755
index 00000000..457cb4b8
--- /dev/null
+++ b/test/pict-roundtrip.test
@@ -0,0 +1,15 @@
+#! /bin/bash
+# This script tests: picttoppm ppmtopict
+# Also requires: pamseq pamdepth pamtopnm pnmremap
+
+  alias picttoppm="${PBM_TESTPREFIX}picttoppm"
+  alias ppmtopict="${PBM_TESTPREFIX}ppmtopict"
+  alias pamseq="${PBM_BINPREFIX}pamseq"
+  alias pamdepth="${PBM_BINPREFIX}pamdepth"
+  alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
+  alias pnmremap="${PBM_BINPREFIX}pnmremap"
+  shopt -s expand_aliases
+
+#Test.  Should print 984199586 101484
+pamseq 3 5 -tupletype=RGB | pamdepth 255 | pamtopnm | \
+  pnmremap -mapfile=- testimg.ppm | ppmtopict | picttoppm | cksum
diff --git a/test/pnminvert.ok b/test/pnminvert.ok
index 081e53ee..6cf5f011 100644
--- a/test/pnminvert.ok
+++ b/test/pnminvert.ok
@@ -1,6 +1,6 @@
 1240379484 41
 1416115901 101484
-2961441369 33838
+1174803406 33838
 2595564405 14
 2595564405 14
 2595564405 14
diff --git a/test/pnminvert.test b/test/pnminvert.test
index eb25d2a0..f779d574 100755
--- a/test/pnminvert.test
+++ b/test/pnminvert.test
@@ -1,25 +1,29 @@
 #! /bin/bash
 # This script tests: pnminvert
-# Also requires: pbmmake ppmtopgm
+# Also requires: pbmmake
 
   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
 pnminvert testgrid.pbm | cksum
+
 # Test 2.  Should print 1416115901 101484
 pnminvert testimg.ppm | cksum
-# Test 3.  Should print 2961441369 33838
-# printed 4215652354 33838 with older ppmtopgm
-ppmtopgm testimg.ppm | pnminvert | cksum
+
+# Test 3.  Should print 
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnminvert | cksum
+
 # Test 4.  Should print 2595564405 14
 pbmmake -w 7 7 | pnminvert | cksum
+
 # Test 5.  Should print 2595564405 14
 pbmmake -b 7 7 | cksum
+
 # Test 6.  Should print 2595564405 14
-pbmmake -b 7 7 | pnminvert | \
-  pnminvert | cksum
+pbmmake -b 7 7 | pnminvert | pnminvert | cksum
+
 # Test 7.  Should print 2896726098 15
 pbmmake -g 8 8 | pnminvert | cksum
diff --git a/test/pnmpsnr.test b/test/pnmpsnr.test
index da88d70d..db1f0079 100755
--- a/test/pnmpsnr.test
+++ b/test/pnmpsnr.test
@@ -6,10 +6,18 @@
   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 | \
+tmpdir=${tmpdir:-/tmp}
+
+
+w_pbm=${tmpdir}/w.pbm
+b_pbm=${tmpdir}/b.pbm
+
+pbmmake -w 10 10 > ${w_pbm}
+pbmmake -b 10 10 > ${b_pbm}
+
+pnmpsnr  ${w_pbm}  ${b_pbm} 2>&1 | \
  awk '{print $(NF-1),$NF}'
-pnmpsnr  ${tmpdir}/w.pbm  ${tmpdir}/w.pbm 2>&1 | \
+pnmpsnr  ${w_pbm}  ${w_pbm} 2>&1 | \
  awk '{print $(NF-1),$NF}'
-rm ${tmpdir}/b.pbm ${tmpdir}/w.pbm
+
+rm ${b_pbm} ${w_pbm}
diff --git a/test/pnmremap1.test b/test/pnmremap1.test
index 208be2fb..20bee75d 100755
--- a/test/pnmremap1.test
+++ b/test/pnmremap1.test
@@ -8,9 +8,12 @@
   alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
   shopt -s expand_aliases
 
-pamseq 3 5 -tupletype=RGB | pamtopnm \
- > ${tmpdir}/palette
-pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
+tmpdir=${tmpdir:-/tmp}
+palette=${tmpdir}/palette 
+#palette255=${tmpdir}/palette255
+
+pamseq 3 5 -tupletype=RGB | pamtopnm > ${palette}
+#pamdepth 255 ${palette} > ${palette255}
 
 # Test 1. Floyd-Steinberg
 # This fails with older versions of Netpbm and x86-64.
@@ -19,7 +22,10 @@ pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
 # x86-32: 2667816854 101482 
 # x86-64: 3602410851 101482
 
-pnmremap -mapfile=${tmpdir}/palette -floyd -norandom \
+pnmremap -mapfile=${palette} -floyd -norandom \
  testimg.ppm | cksum
 
-rm ${tmpdir}/palette{,255}
+#pnmremap -mapfile=${palette255} -floyd -norandom \
+# testimg.ppm | cksum
+
+rm ${palette} # ${palette255}
diff --git a/test/pnmremap2.test b/test/pnmremap2.test
index 3399dc39..e5444fda 100755
--- a/test/pnmremap2.test
+++ b/test/pnmremap2.test
@@ -8,20 +8,23 @@
   alias pamtopnm="${PBM_BINPREFIX}pamtopnm"
   shopt -s expand_aliases
 
-pamseq 3 5 -tupletype=RGB | pamtopnm \
- > ${tmpdir}/palette
-pamdepth 255 ${tmpdir}/palette > ${tmpdir}/palette255
+tmpdir=${tmpdir:-/tmp}
+palette=${tmpdir}/palette 
+palette255=${tmpdir}/palette255
+
+pamseq 3 5 -tupletype=RGB | pamtopnm > ${palette}
+pamdepth 255 ${palette} > ${palette255}
 
 # Test 2. Default (unmodified quantization)
-pnmremap -mapfile=${tmpdir}/palette -nofloyd \
+pnmremap -mapfile=${palette} -nofloyd \
 testimg.ppm | cksum
 
 # Test 3. Use first color in palette for missing colors
-pnmremap -mapfile=${tmpdir}/palette255 -nofloyd \
+pnmremap -mapfile=${palette255} -nofloyd \
  -firstisdefault testimg.ppm | cksum
 
 # Test 4. Use black for missing colors
-pnmremap -mapfile=${tmpdir}/palette255 -nofloyd \
+pnmremap -mapfile=${palette255} -nofloyd \
 -missingcolor=black testimg.ppm | cksum
 
-rm ${tmpdir}/palette{,255}
+rm ${palette} ${palette255}
diff --git a/test/pnmtile.test b/test/pnmtile.test
index f74bdce3..40d9cfc2 100755
--- a/test/pnmtile.test
+++ b/test/pnmtile.test
@@ -6,12 +6,20 @@
   alias pnmcat="${PBM_BINPREFIX}pnmcat"
   shopt -s expand_aliases
 
+# Test 1.  Should print 4228632379 259
 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
+tmpdir=${tmpdir:-/tmp}
+
+# Test 2.  Compare 2x2 tile images produced by pnmtile and pnmcat
+# Should print 0
+testimg2_ppm=${tmpdir}/testimg2.ppm
+testimg4_ppm=${tmpdir}/testimg4.ppm
+
+pnmtile 454 298 testimg.ppm > ${testimg4_ppm} &&
+pnmcat -lr testimg.ppm testimg.ppm > ${testimg2_ppm} &&
+pnmcat -tb ${testimg2_ppm} ${testimg2_ppm} | \
+cmp -s - ${testimg4_ppm}
 echo $?
 
-rm ${tmpdir}/testimg{2,4}.ppm
+rm ${testimg2_ppm} ${testimg4_ppm}
diff --git a/test/ppmcie.test b/test/ppmcie.test
index c4338195..973787ec 100755
--- a/test/ppmcie.test
+++ b/test/ppmcie.test
@@ -7,6 +7,12 @@
   alias pamsumm="${PBM_BINPREFIX}pamsumm"
   shopt -s expand_aliases
 
+# Failure message
+## Ppmcie is sensitive to system factors.  If this test fails, please
+## run the program and visually examine the output.
+
+tmpdir=${tmpdir:-/tmp}
+
 # Test 1. Should print 955840041 786447
 # Without -nolabel -noaxes -nowpoint -noblack older versions of
 # Netpbm produce slightly different charts.
@@ -14,8 +20,10 @@
 # v. 10.35.86: 288356530 786447   
 # v. 10.59.2 : 2292601420 786447  
 
+ppmcie_ppm=${tmpdir}/ppmcie.ppm
+
 ppmcie -nolabel -noaxes -nowpoint -noblack \
- > ${tmpdir}/ppmcie.ppm
+ > ${ppmcie_ppm}
 
 # There is a slight difference in the output depending on whether ppmcie
 # is compiled with SSE features are turned on or off.
@@ -31,7 +39,7 @@ ppmcie -nolabel -noaxes -nowpoint -noblack \
 # x86 32 bit: 38.660173
 # x86 64 bit: 38.681432
 
-pamsumm --mean --brief ${tmpdir}/ppmcie.ppm | \
+pamsumm --mean --brief ${ppmcie_ppm} | \
   awk '{ if(38.65 < $1 && $1 <38.69) print "ok"; else print $1}'
 
 # Test 2.  Measure image sharpness
@@ -39,9 +47,9 @@ pamsumm --mean --brief ${tmpdir}/ppmcie.ppm | \
 # x86 32 bit: 0.002476
 # x86 64 bit: 0.002478
 
-pamsharpness ${tmpdir}/ppmcie.ppm 2>&1 | \
+pamsharpness ${ppmcie_ppm} 2>&1 | \
   awk 'NF==3 && $1=="Sharpness" \
        {if (0.002475 < $3 && $3 < 0.002479) print "ok"; else print $3}
        NF>0 && NF!=3 {print "error"}'
 
-rm ${tmpdir}/ppmcie.ppm
+rm ${ppmcie_ppm}
diff --git a/test/ppmdim.test b/test/ppmdim.test
index e528c3ce..cdd7df17 100755
--- a/test/ppmdim.test
+++ b/test/ppmdim.test
@@ -8,18 +8,23 @@
   alias pnmarith="${PBM_BINPREFIX}pnmarith"
   shopt -s expand_aliases
 
+tmpdir=${tmpdir:-/tmp}
+
 # 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.
 # If the mean difference is between 0 and 0.75 we consider the output
 # normal.  This works for dim values up to 0.994 .
+dim1_ppm=${tmpdir}/dim1.ppm
+dim2_ppm=${tmpdir}/dim2.ppm
 
 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
-  ppmdim $i testimg.ppm > ${tmpdir}/dim1.ppm
-  pamfunc -mult=$i testimg.ppm > ${tmpdir}/dim2.ppm
-  pnmarith -diff ${tmpdir}/dim1.ppm ${tmpdir}/dim2.ppm | \
+  ppmdim $i testimg.ppm > ${dim1_ppm}
+  pamfunc -mult=$i testimg.ppm > ${dim2_ppm}
+  pnmarith -diff ${dim1_ppm} ${dim2_ppm} | \
     pamsumm -mean -brief | \
     awk '{print $1<0.75 ? "ok" : "fail"}'
   done
-rm ${tmpdir}/dim[12].ppm
+
+rm ${dim1_ppm} ${dim2_ppm}
diff --git a/test/ppmmix.test b/test/ppmmix.test
index d24e589d..25e35f0e 100755
--- a/test/ppmmix.test
+++ b/test/ppmmix.test
@@ -13,14 +13,19 @@
   alias ppmtopgm="${PBM_BINPREFIX}ppmtopgm"
   shopt -s expand_aliases
 
-# Print a pretty checkerboard pattern
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1. Print a pretty checkerboard pattern
+a1_pgm=${tmpdir}/a1.pgm
+a2_pgm=${tmpdir}/a2.pgm
+
 pbmmake -g 8 8 | \
- pgmtopgm > ${tmpdir}/a1.pgm &&
+ pgmtopgm > ${a1_pgm} &&
 pbmmake -g 2 2 | pamenlarge 4 | \
- pgmtopgm > ${tmpdir}/a2.pgm &&
-ppmmix 0.75 ${tmpdir}/a1.pgm ${tmpdir}/a2.pgm -plain | \
+ pgmtopgm > ${a2_pgm} &&
+ppmmix 0.75 ${a1_pgm} ${a2_pgm} -plain | \
  ppmtopgm | pamdepth 3 -plain &&
-rm ${tmpdir}/a1.pgm ${tmpdir}/a2.pgm
+rm ${a1_pgm} ${a2_pgm}
 
 # Mix image with itself.
 # Output should match input regardless of ratio. 
@@ -31,8 +36,10 @@ done
 
 # Mix image with its own inverse.
 # Output should be a monotone gray sheet.
+a3_ppm=${tmpdir}/a3.ppm
+
 pnminvert testimg.ppm | ppmmix .5 \
-    testimg.ppm - | tee ${tmpdir}/a3.ppm | \
+    testimg.ppm - | tee ${a3_ppm} | \
   pamsumm -brief -max &&
-  pamsumm -brief -min ${tmpdir}/a3.ppm &&
-rm ${tmpdir}/a3.ppm
+  pamsumm -brief -min ${a3_ppm} &&
+rm ${a3_ppm}
diff --git a/test/ps-alt-roundtrip.test b/test/ps-alt-roundtrip.test
index 8c4cfc2a..d61e55d9 100755
--- a/test/ps-alt-roundtrip.test
+++ b/test/ps-alt-roundtrip.test
@@ -19,38 +19,49 @@
 #
 # pbmtopsg3 and pbmtolps produce output that require pstopnm for decoding.
 #
-# If ps-roundtrip.test succeeds and this test fails, it is most likely
-# a problem with one of the minor utilities, and vice versa.
+# Failure message
+## If ps-roundtrip.test succeeds and this test fails, it is most likely
+## a problem with one of the alternate Postscipt utilities:
+## pbmtoepsi, pbmtopsg3, pbmtolps or psidtopgm.
+## If both tests fail it indicates a problem with pstopnm or gs.
 
 # pstopnm does not use libnetpbm functions for output. 
 # Output is filtered through pnmtopnm.
 
 # Test 1. Should print: 2425386270 41
+testgrid1_ps=${tmpdir}/testgrid1.ps
+
 pbmtopsg3 -dpi=72 testgrid.pbm \
-     > ${tmpdir}/testgrid1.ps && \
+     > ${testgrid1_ps} && \
 pstopnm -xborder=0 -yborder=0 -llx=0 -lly=-16 -urx=14 \
-    -dpi=72 -stdout -quiet -pbm ${tmpdir}/testgrid1.ps | \
+    -dpi=72 -stdout -quiet -pbm ${testgrid1_ps} | \
     pnmcrop | cksum
 
+rm ${testgrid1_ps}
+
 
 # Test 2. Should print: 2425386270 41
+testgrid2_ps=${tmpdir}/testgrid2.ps
+
 pbmtolps -dpi 72 testgrid.pbm \
-     > ${tmpdir}/testgrid2.ps && \
+     > ${testgrid2_ps} && \
 pstopnm -xborder=0 -yborder=0 -dpi=72 -stdout \
-    -quiet ${tmpdir}/testgrid2.ps -pbm | \
+    -quiet ${testgrid2_ps} -pbm | \
   pnmcrop | cksum
 
+rm ${testgrid2_ps}
 
 # Test 3. Should print: 2916080186 235
 # Output is pgm maxval=1 with black and white inverted.
 #
-pbmtoepsi testgrid.pbm > ${tmpdir}/testgrid.epsi && \
+testgrid_epsi=${tmpdir}/testgrid.epsi 
+
+pbmtoepsi testgrid.pbm > ${testgrid_epsi} && \
 xysizebps=`awk  '/BeginPreview/ {print $2,$3,$4}' \
-    ${tmpdir}/testgrid.epsi` && \
+    ${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 | psidtopgm $xysizebps | cksum
-
+    ${testgrid_epsi} | psidtopgm $xysizebps | cksum
 
-rm ${tmpdir}/testgrid[12].ps  ${tmpdir}/testgrid.epsi 
+rm ${testgrid_epsi} 
diff --git a/test/ps-roundtrip.test b/test/ps-roundtrip.test
index 1877724d..4773d4a6 100755
--- a/test/ps-roundtrip.test
+++ b/test/ps-roundtrip.test
@@ -16,46 +16,53 @@
 ## (1) zlib was not linked.
 ## (2) ghostscript is not available.
 
+tmpdir=${tmpdir:-/tmp}
+
 # 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.
+test1_ps=${tmpdir}/testimg1.ps
+
 for flag in "" "-ps" "-rle" "-ps -ascii" "-ps -flate"
   do
-  pnmtops -nocenter -equalpixels -dpi 72 -noturn \
-    ${flag} testimg.ppm \
-    > ${tmpdir}/testimg.ps
+  pnmtops -nocenter -equalpixels -dpi 72 -noturn ${flag} testimg.ppm \
+    > ${test1_ps} && \
   xysize1=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \
-    ${tmpdir}/testimg.ps` 
-  pstopnm -portrait -xborder=0 -yborder=0 $xysize1 -stdout \
-    -quiet ${tmpdir}/testimg.ps | \
-    pnmtopnm | cksum
+    ${test1_ps}` && \
+  pstopnm -portrait -xborder=0 -yborder=0 $xysize1 -stdout -quiet \
+    ${test1_ps} | pnmtopnm | cksum
   done
 
-
+rm ${test1_ps}
 # Test 2.  Should print: 2918318199 62 seven times
 # Test image designed to detect problems with run-length compression
 #
-pbmmake -g 2 2 > ${tmpdir}/g.pbm
+
+g_pbm=${tmpdir}/g.pbm
+t_pbm=${tmpdir}/t.pbm
+grid_ps=${tmpdir}/testgrid.ps
+
+pbmmake -g 2 2 > ${g_pbm}
 pbmmake -g 8 4 | \
   pnmshear 45 -noantialias -background=black | \
   pnmpad -right 60 | \
-  pnmcat -tb -jright - ${tmpdir}/g.pbm > ${tmpdir}/t.pbm &&
+  pnmcat -tb -jright - ${g_pbm} > ${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
-  pnmtops -nocenter -equalpixels -dpi 72 -noturn \
-    ${flag} ${tmpdir}/t.pbm  > ${tmpdir}/testgrid.ps &&
+  pnmtops -nocenter -equalpixels -dpi 72 -noturn  ${flag} ${t_pbm} \
+    > ${grid_ps} && \
   xysize2=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \
-    ${tmpdir}/testgrid.ps`
+      ${grid_ps}` && \
   pstopnm -portrait -xborder=0 -yborder=0 $xysize2 -stdout \
-    -quiet ${tmpdir}/testgrid.ps -pbm | \
-    pnmtopnm | cksum
+    -quiet ${grid_ps} -pbm | pnmtopnm | cksum
   done
 
+rm ${grid_ps} ${g_pbm} ${t_pbm}
 
 #Test 3. Should print: 1386192571 507420 three times
 # *NOTE* Second iteration fails if pnmtops was compiled without zlib
@@ -69,19 +76,19 @@ for flag in "" "-rle" "-ps -rle -ascii" \
 # (3) pstopnm: input must be an ordinary file.  Input from stdin
 #     (by pipe or input redirection: "< file" ) does not work.              
 #
+
+test3_ps=${tmpdir}/testimg3.ps
+
 for flag in "" "-ps" \
             "-ps -bitspersample=12 -flate -rle -vmreclaim"
   do
 cat testimg.ppm testimg.ppm testimg.ppm testgrid.pbm testgrid.pbm | \
-pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage \
-  ${flag}  > ${tmpdir}/testimg5.ps
+pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage ${flag} \
+  > ${test3_ps} &&
 xysize3=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5 ; exit}' \
-  ${tmpdir}/testimg5.ps`
-pstopnm -portrait -xborder=0 -yborder=0 $xysize3 \
-    -stdout  ${tmpdir}/testimg5.ps | \
+  ${test3_ps}` &&
+pstopnm -portrait -xborder=0 -yborder=0 $xysize3 -stdout  ${test3_ps} | \
   pnmtopnm | cksum
   done
 
-
-rm ${tmpdir}/testgrid.ps  ${tmpdir}/testimg.ps  ${tmpdir}/testimg5.ps \
-   ${tmpdir}/t.pbm
+rm ${test3_ps}
diff --git a/test/rgb3-roundtrip.test b/test/rgb3-roundtrip.test
index 829feab4..091fe46b 100755
--- a/test/rgb3-roundtrip.test
+++ b/test/rgb3-roundtrip.test
@@ -13,29 +13,40 @@
 # image from them and check whether the resulting output is
 # identical to the original input.
 
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1.  PPM (color) input
+testimg_ppm=${tmpdir}/testimg.ppm
+testimg_red=${tmpdir}/testimg.red
+testimg_grn=${tmpdir}/testimg.grn
+testimg_blu=${tmpdir}/testimg.blu
+
 cp testimg.ppm ${tmpdir} &&
-ppmtorgb3 ${tmpdir}/testimg.ppm &&
-rgb3toppm ${tmpdir}/testimg.red ${tmpdir}/testimg.grn \
-  ${tmpdir}/testimg.blu | cksum
+ppmtorgb3 ${testimg_ppm} &&
+rgb3toppm ${testimg_red} ${testimg_grn} ${testimg_blu} | cksum
 
-cat ${tmpdir}/testimg.red ${tmpdir}/testimg.grn ${tmpdir}/testimg.blu | \
-  cksum
+cat ${testimg_red} ${testimg_grn} ${testimg_blu} | cksum
 
-rm ${tmpdir}/testimg.{ppm,red,grn,blu}
+rm ${testimg_ppm} ${testimg_red} ${testimg_grn} ${testimg_blu}
+
+# Test 2.  PBM (monochrome) input
+testgrid_pbm=${tmpdir}/testgrid.pbm
+testgrid_red=${tmpdir}/testgrid.red
+testgrid_grn=${tmpdir}/testgrid.grn
+testgrid_blu=${tmpdir}/testgrid.blu
 
 cp testgrid.pbm ${tmpdir} &&
-ppmtorgb3 ${tmpdir}/testgrid.pbm &&
-rgb3toppm ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn \
-  ${tmpdir}/testgrid.blu | \
+ppmtorgb3 ${testgrid_pbm} &&
+rgb3toppm ${testgrid_red} ${testgrid_grn} ${testgrid_blu} | \
   ppmtopgm | pgmtopbm -th -val=0.5 | cksum
 
-
+# Test 3.
 # With PGM or PBM input, the three monochrome planes should be
 # identical.  Test for this.
 
-cmp -s ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn ; echo $?
-cmp -s ${tmpdir}/testgrid.grn ${tmpdir}/testgrid.blu ; echo $?
-pgmtopgm < testgrid.pbm | cmp -s - ${tmpdir}/testgrid.red
+cmp -s ${testgrid_red} ${testgrid_grn} ; echo $?
+cmp -s ${testgrid_grn} ${testgrid_blu} ; echo $?
+pgmtopgm < testgrid.pbm | cmp -s - ${testgrid_red}
   echo $?
 
-rm ${tmpdir}/testgrid.{pbm,red,grn,blu}
+rm ${testgrid_pbm} ${testgrid_red} ${testgrid_grn} ${testgrid_blu}
diff --git a/test/sgi-roundtrip.ok b/test/sgi-roundtrip.ok
new file mode 100644
index 00000000..541d59b1
--- /dev/null
+++ b/test/sgi-roundtrip.ok
@@ -0,0 +1,6 @@
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+538848130 235
+538848130 235
diff --git a/test/sgi-roundtrip.test b/test/sgi-roundtrip.test
new file mode 100755
index 00000000..99b5735e
--- /dev/null
+++ b/test/sgi-roundtrip.test
@@ -0,0 +1,38 @@
+#! /bin/bash
+# This script tests: pnmtosgi sgitopnm
+# Also requires: rgb3toppm
+
+  alias pnmtosgi="${PBM_TESTPREFIX}pnmtosgi"
+  alias sgitopnm="${PBM_TESTPREFIX}sgitopnm"
+  alias rgb3toppm="${PBM_BINPREFIX}rgb3toppm"
+  shopt -s expand_aliases
+
+a_sgi=${tmpdir}/a.sgi
+a_red=${tmpdir}/a.red
+a_grn=${tmpdir}/a.grn
+a_blu=${tmpdir}/a.blu
+
+# Test 1.  Should produce 1926073387 101484 twice
+pnmtosgi -rle testimg.ppm | tee ${a_sgi} | sgitopnm  | cksum
+sgitopnm -channel=0  ${a_sgi} > ${a_red}
+sgitopnm -channel=1  ${a_sgi} > ${a_grn}
+sgitopnm -channel=2  ${a_sgi} > ${a_blu}
+rgb3toppm ${a_red} ${a_grn} ${a_blu} | cksum
+rm ${a_sgi} ${a_red} ${a_grn} ${a_blu}
+
+b_sgi=${tmpdir}/b.sgi
+b_red=${tmpdir}/b.red
+b_grn=${tmpdir}/b.grn
+b_blu=${tmpdir}/b.blu
+
+# Test 2.  Should produce 1926073387 101484 twice
+pnmtosgi -verbatim testimg.ppm | tee ${b_sgi} | sgitopnm  | cksum
+sgitopnm -channel=0  ${b_sgi} > ${b_red}
+sgitopnm -channel=1  ${b_sgi} > ${b_grn}
+sgitopnm -channel=2  ${b_sgi} > ${b_blu}
+rgb3toppm ${b_red} ${b_grn} ${b_blu} | cksum
+rm ${b_sgi} ${b_red} ${b_grn} ${b_blu}
+
+# Test 3.  Should produce 2425386270 41 twice
+pnmtosgi testgrid.pbm | sgitopnm | cksum             # Defaults to -rle
+pnmtosgi -verbatim testgrid.pbm | sgitopnm | cksum
diff --git a/test/targa-roundtrip.ok b/test/targa-roundtrip.ok
index 2b4b6eb9..9a428195 100644
--- a/test/targa-roundtrip.ok
+++ b/test/targa-roundtrip.ok
@@ -1,3 +1,3 @@
 2425386270 41
-2871603838 33838
+1571496937 33838
 1926073387 101484
diff --git a/test/targa-roundtrip.test b/test/targa-roundtrip.test
index 4a99e0e0..66e666c6 100755
--- a/test/targa-roundtrip.test
+++ b/test/targa-roundtrip.test
@@ -1,11 +1,10 @@
 #! /bin/bash
 # This script tests: pamtotga tgatoppm
-# Also requires: ppmtopgm pgmtopbm
+# Also requires: 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
@@ -14,13 +13,9 @@ pamtotga -mono testgrid.pbm | \
   tgatoppm | ppmtopgm | \
   pgmtopbm -threshold -val 0.5 | cksum
 
-#Test 2: Should print 2871603838 33838, cksum of testimg.pgm
-
-ppmtopgm testimg.ppm > ${tmpdir}/testimg.pgm
-pamtotga -cmap ${tmpdir}/testimg.pgm | \
-  tgatoppm | ppmtopgm | cksum
-
-rm ${tmpdir}/testimg.pgm
+#Test 2:  Should produce 1571496937 33838, cksum of testimg.red
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | \
+  pamtotga -cmap | tgatoppm | ppmtopgm | cksum
 
 #Test 3: Should print 1926073387 101484, cksum of testimg.ppm
 
diff --git a/test/utahrle-roundtrip.ok b/test/utahrle-roundtrip.ok
index 35fc434c..203001aa 100644
--- a/test/utahrle-roundtrip.ok
+++ b/test/utahrle-roundtrip.ok
@@ -1,2 +1,2 @@
-2871603838 33838
+1571496937 33838
 1926073387 101484
diff --git a/test/utahrle-roundtrip.test b/test/utahrle-roundtrip.test
index 83bc6c90..dd88265c 100755
--- a/test/utahrle-roundtrip.test
+++ b/test/utahrle-roundtrip.test
@@ -1,18 +1,14 @@
 #! /bin/bash
 # This script tests: pnmtorle rletopnm
-# Also requires: ppmtopgm
+# Also requires: 
 
   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
-ppmtopgm testimg.ppm > ${tmpdir}/testimg.pgm
-pnmtorle ${tmpdir}/testimg.pgm | \
-  rletopnm | cksum
-
-rm ${tmpdir}/testimg.pgm
+#Test 1.  Should produce 1571496937 33838, cksum of testimg.red
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnmtorle | rletopnm | cksum
 
 #Test 2.  Should print 1926073387 101484, cksum of testimg.ppm
 pnmtorle testimg.ppm | \
diff --git a/test/xwd-roundtrip.ok b/test/xwd-roundtrip.ok
index 011ebd3a..25d3d871 100644
--- a/test/xwd-roundtrip.ok
+++ b/test/xwd-roundtrip.ok
@@ -1,3 +1,3 @@
-2871603838 33838
+1571496937 33838
 1926073387 101484
 2425386270 41
diff --git a/test/xwd-roundtrip.test b/test/xwd-roundtrip.test
index b5614f01..4dbd3020 100755
--- a/test/xwd-roundtrip.test
+++ b/test/xwd-roundtrip.test
@@ -1,17 +1,15 @@
 #! /bin/bash
 # This script tests: pnmtoxwd xwdtopnm
-# Also requires: pamdepth ppmtopgm
+# Also requires: pamdepth 
 
   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
-ppmtopgm testimg.ppm | pnmtoxwd  | \
-  xwdtopnm | pamdepth 255 | cksum
+# Test 1.  Should produce 1571496937 33838, cksum of testimg.red
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnmtoxwd | xwdtopnm | pamdepth 255 | cksum
 
 # Test 2.  Should produce 1926073387 101484
 pnmtoxwd --quiet  testimg.ppm | \
diff --git a/version.mk b/version.mk
index 3db36b97..598ff968 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 67
-NETPBM_POINT_RELEASE = 6
+NETPBM_MINOR_RELEASE = 68
+NETPBM_POINT_RELEASE = 0