about summary refs log tree commit diff
path: root/generator
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-03-27 01:46:26 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-03-27 01:46:26 +0000
commitdff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa (patch)
treeb147568ccffc4cdba9e2a98de1452450ba8e55c3 /generator
parent4ce684c4978610d1ea42be1b00f7332f3f5f337a (diff)
downloadnetpbm-mirror-dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa.tar.gz
netpbm-mirror-dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa.tar.xz
netpbm-mirror-dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa.zip
Promote Advanced (10.73) to Stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/stable@2692 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'generator')
-rw-r--r--generator/Makefile7
-rw-r--r--generator/pamcrater.c514
-rw-r--r--generator/pamgauss.c20
-rw-r--r--generator/pamgradient.c8
-rw-r--r--generator/pamseq.c7
-rw-r--r--generator/pamshadedrelief.c250
-rw-r--r--generator/pamstereogram.c947
-rw-r--r--generator/pbmmake.c25
-rw-r--r--generator/pbmmake.test9
-rw-r--r--generator/pbmpage.c251
-rw-r--r--generator/pbmtext.c9
-rw-r--r--generator/pbmtextps.c39
-rwxr-xr-xgenerator/pgmcrater94
-rw-r--r--generator/pgmcrater.c382
-rw-r--r--generator/pgmkernel.c288
-rw-r--r--generator/pgmmake.c20
-rw-r--r--generator/pgmnoise.c105
-rw-r--r--generator/pgmramp.c62
-rw-r--r--generator/ppmcie.c351
-rw-r--r--generator/ppmcolors.c9
-rw-r--r--generator/ppmforge.c417
-rw-r--r--generator/ppmmake.c20
-rw-r--r--generator/ppmpat.c42
-rwxr-xr-xgenerator/ppmrainbow26
-rw-r--r--generator/ppmrough.c530
25 files changed, 2929 insertions, 1503 deletions
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..50745501
--- /dev/null
+++ b/generator/pamcrater.c
@@ -0,0 +1,514 @@
+/*=============================================================================
+                               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 verbose;
+    unsigned int test;
+    unsigned int radius;
+    int          offset;
+};
+
+
+
+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, offsetSpec;
+
+    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,   "verbose",    OPT_FLAG,    NULL,
+            &cmdlineP->verbose,          0);
+    OPTENT3(0,   "test",       OPT_FLAG,    NULL,
+            &cmdlineP->test,       0);
+    OPTENT3(0,   "radius",     OPT_UINT,    &cmdlineP->radius,
+            &radiusSpec,           0);
+    OPTENT3(0,   "offset",     OPT_INT,     &cmdlineP->offset,
+            &offsetSpec,           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 (!offsetSpec)
+        cmdlineP->offset=0;
+
+    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 (offsetSpec)
+            pm_error("-offset is meaningful only with -test");
+
+        if (!numberSpec)
+            cmdlineP->number = 50000;
+
+        if (cmdlineP->number == 0)
+            pm_error("-number must be positive");
+    }
+    free(option_def);
+}
+
+
+
+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) {
+/*----------------------------------------------------------------------------
+   A random number in the range [0, '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) {
+/*----------------------------------------------------------------------------
+   A pointer to the sample in 'terrain' of an image described by *pamP that is
+   at Column 'x' of Row 'y', but modulus the image size.
+
+   So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value
+   would be a pointer to the sample at Column 2 or Row 2.  If they are both
+   -1, we would point to Column 9, Row 9.
+-----------------------------------------------------------------------------*/
+    return &terrain[mod(x, pamP->height)][mod(y, pamP->width)][0];
+}
+
+
+
+
+static sample
+terrainMod(struct pam * const pamP,
+           tuple **     const terrain,
+           int          const x,
+           int          const y) {
+/*----------------------------------------------------------------------------
+   The value of the sample in 'terrain' of an image described by *pamP that is
+   at Column 'x' of Row 'y', but modulus the image size.
+
+   So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value
+   would be the value of the sample at Column 2 or Row 2.  If they are both
+   -1, we would return Column 9, Row 9.
+-----------------------------------------------------------------------------*/
+    return *terrainModP(pamP, terrain, x, y);
+}
+
+
+
+static void
+setElev(struct pam * const pamP,
+        tuple **     const terrain,
+        int          const cx,
+        int          const cy,
+        unsigned int const elevation) {
+
+    *terrainModP(pamP, terrain, cx, cy) = MIN(pamP->maxval, elevation);
+}
+
+
+
+static void
+smallCrater(struct pam * const pamP,
+            tuple **     const terrain,
+            int          const cx,
+            int          const cy,
+            double       const radius) {
+/*----------------------------------------------------------------------------
+   Generate a crater with a special method for tiny craters.
+
+   Center the crater at Column 'cx', Row 'cy'; wrap as necessary to get them
+   on the canvas.  These might even be negative.
+-----------------------------------------------------------------------------*/
+    int y;
+    unsigned int amptot;
+    unsigned int npatch;
+
+    assert(radius < 3);
+
+    /* 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;
+            /* The mean elevation of the Moore neighborhood (9 pixels
+               centered on the crater location).
+            */
+        
+        /* Perturb the mean elevation by a small random factor. */
+
+        int const x = radius >= 1 ? ((rand() >> 8) & 0x3) - 1 : 0;
+
+        assert(axelev > 0);
+
+        setElev(pamP, terrain, cx, cy, axelev + x);
+    }
+}
+
+
+
+static unsigned int
+meanElev(struct pam * const pamP,
+         tuple **     const terrain,
+         int          const cx,
+         int          const cy,
+         double       const radius) {
+/*----------------------------------------------------------------------------
+   The mean elevation in 'terrain', which is described by *pamP, within
+   'radius' pixels vertically and horizontally of (cx, cy).
+
+   We assume the area is a fraction the whole 'terrain'.
+-----------------------------------------------------------------------------*/
+    unsigned int amptot;
+    unsigned int npatch;
+    int y;
+
+    for (y = cy - radius, amptot = 0, npatch = 0; y <= cy + radius; ++y) {
+        int x;
+        for (x = cx - radius; x <= cx + radius; ++x) {
+            amptot += terrainMod(pamP, terrain, x, y);
+            ++npatch;
+        }
+    }
+    assert(npatch > 0);
+
+    return amptot / npatch;
+}
+
+
+
+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 const axelev = meanElev(pamP, terrain, cx, cy, impactRadius);
+        /* The mean elevation of the impact area, before impact */
+
+    for (y = cy - craterRadius; y <= cy + craterRadius; ++y) {
+        int const dysq = SQR(cy - y);
+
+        int x;
+
+        for (x = cx - craterRadius; x <= cx + craterRadius; ++x) {
+            int  const dxsq = SQR(cx - x);
+            double const cd = (dxsq + dysq) / (double) SQR(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));
+                
+                setElev(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,
+           bool         const verbose) {
+
+    if (verbose && pm_have_float_format())
+        pm_message("Plopping crater at (%4d, %4d) with radius %g",
+                   cx, cy, radius);
+
+    if (radius < 3)
+        smallCrater (pamP, terrain, cx, cy, radius);
+    else
+        normalCrater(pamP, terrain, cx, cy, radius);
+}
+
+
+
+static void
+initCanvas(unsigned int const width,
+           unsigned int const height,
+           struct pam * const pamP,
+           tuple ***    const terrainP) {
+/*----------------------------------------------------------------------------
+   Initialize the output image to a flat area of middle elevation.
+-----------------------------------------------------------------------------*/
+    tuple ** terrain;    /* elevation array */
+    unsigned int row;
+
+    pamP->size   = sizeof(*pamP);
+    pamP->len    = PAM_STRUCT_SIZE(tuple_type);
+    pamP->file   = stdout;
+    pamP->format = PAM_FORMAT;
+    pamP->height = height;
+    pamP->width  = width;
+    pamP->depth  = 1;
+    pamP->maxval = 65535;
+    pamP->bytes_per_sample = 2;
+    STRSCPY(pamP->tuple_type, "elevation");
+
+    terrain = pnm_allocpamarray(pamP);
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col)
+            terrain[row][col][0] = pamP->maxval / 2;
+    }
+    *terrainP = terrain;
+}
+
+
+
+static void
+genCraters(struct CmdlineInfo const cmdline) {
+/*----------------------------------------------------------------------------
+   Generate cratered terrain
+-----------------------------------------------------------------------------*/
+    tuple ** terrain;    /* elevation array */
+    struct pam pam;
+
+    /* Allocate the elevation array and initialize it to mean surface
+       elevation.
+    */
+
+    initCanvas(cmdline.width, cmdline.height, &pam, &terrain);
+
+    if (cmdline.test)
+        plopCrater(&pam, terrain,
+                   pam.width/2 + cmdline.offset,
+                   pam.height/2 + cmdline.offset,
+                   (double) cmdline.radius, cmdline.verbose);
+    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, cmdline.verbose);
+
+            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/pamgauss.c b/generator/pamgauss.c
index 2b715467..82c340fa 100644
--- a/generator/pamgauss.c
+++ b/generator/pamgauss.c
@@ -9,11 +9,9 @@
 #include "mallocvar.h"
 #include "pam.h"
 
-#define true (1)
-#define false (0)
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -27,8 +25,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
   Convert program invocation arguments (argc,argv) into a format the 
   program can use easily, struct cmdlineInfo.  Validate arguments along
@@ -59,7 +57,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -68,7 +66,8 @@ parseCommandLine(int argc, char ** argv,
         struct pam pam;
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("The tuple type you specified is too long.  "
-                     "Maximum %d characters.", sizeof(pam.tuple_type)-1);
+                     "Maximum %u characters.",
+                     (unsigned)sizeof(pam.tuple_type)-1);
     }        
 
     if (!sigmaSpec)
@@ -102,6 +101,7 @@ parseCommandLine(int argc, char ** argv,
             pm_error("height argument must be a positive number.  You "
                      "specified '%s'", argv[2]);
     }
+    free(option_def);
 }
 
 
@@ -160,15 +160,15 @@ imageNormalizer(struct pam * const pamP,
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam pam;
     int row;
     double normalizer;
     tuplen * tuplerown;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
    
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/generator/pamgradient.c b/generator/pamgradient.c
index aa559d27..57e78288 100644
--- a/generator/pamgradient.c
+++ b/generator/pamgradient.c
@@ -18,7 +18,7 @@ struct cmdlineInfo {
 };
 
 static void
-parseCommandLine(int argc, char **argv,
+parseCommandLine(int argc, const char **argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
   Convert program invocation arguments (argc,argv) into a format the 
@@ -45,7 +45,7 @@ parseCommandLine(int argc, char **argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!maxvalSpec)
@@ -153,7 +153,7 @@ createEdge(const struct pam * const pamP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
     struct pam pam;
@@ -162,7 +162,7 @@ main(int argc, char *argv[]) {
     tuple * rightEdge;
     unsigned int row;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/generator/pamseq.c b/generator/pamseq.c
index 98eac1cc..1af5252a 100644
--- a/generator/pamseq.c
+++ b/generator/pamseq.c
@@ -7,8 +7,6 @@
 #include "pam.h"
 #include "shhopt.h"
 
-#define true (1)
-#define false (0)
 
 
 struct cmdlineInfo {
@@ -50,7 +48,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -59,7 +57,8 @@ parseCommandLine(int argc, char ** argv,
         struct pam pam;
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("The tuple type you specified is too long.  "
-                     "Maximum %d characters.", sizeof(pam.tuple_type)-1);
+                     "Maximum %u characters.", 
+                     (unsigned)sizeof(pam.tuple_type)-1);
     }        
 
     if (argc-1 < 2)
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/pamstereogram.c b/generator/pamstereogram.c
index 0ce63853..6e5f5ce0 100644
--- a/generator/pamstereogram.c
+++ b/generator/pamstereogram.c
@@ -8,12 +8,15 @@
  * The core of this program is a simple adaptation of the code in
  * "Displaying 3D Images: Algorithms for Single Image Random Dot
  * Stereograms" by Harold W. Thimbleby, Stuart Inglis, and Ian
- * H. Witten in IEEE Computer, 27(10):38-48, October 1994.  See that
- * paper for a thorough explanation of what's going on here.
+ * H. Witten in IEEE Computer, 27(10):38-48, October 1994 plus some
+ * enhancements presented in "Stereograms: Technical Details" by
+ * W. A. Steer at http://www.techmind.org/stereo/stech.html.  See
+ * those references for a thorough explanation of what's going on
+ * here.
  *
  * ----------------------------------------------------------------------
  *
- * Copyright (C) 2006 Scott Pakin <scott+pbm@pakin.org>
+ * Copyright (C) 2006-2015 Scott Pakin <scott+pbm@pakin.org>
  *
  * All rights reserved.
  *
@@ -43,20 +46,21 @@
  * ----------------------------------------------------------------------
  */
 
+#define _ISOC99_SOURCE  /* Make sure strtof() is in <stdlib.h> */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <limits.h>
 #include <assert.h>
 
 #include "pm_config.h"
 #include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
 
-/* Define a few helper macros. */
-#define round2int(X) ((int)((X)+0.5))      /* Nonnegative numbers only */
 
 enum outputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR};
 
@@ -71,25 +75,65 @@ struct cmdlineInfo {
     unsigned int crosseyed;      /* -crosseyed option */
     unsigned int makemask;       /* -makemask option */
     unsigned int dpi;            /* -dpi option */
-    float eyesep;                /* -eyesep option */
-    float depth;                 /* -depth option */
+    float        eyesep;         /* -eyesep option */
+    float        depth;          /* -depth option */
     unsigned int maxvalSpec;     /* -maxval option count */
-    unsigned int maxval;         /* -maxval option value x*/
-    int guidesize;               /* -guidesize option */
+    unsigned int maxval;         /* -maxval option value */
+    unsigned int guidetop;       /* -guidetop option count */
+    unsigned int guidebottom;    /* -guidebottom option count */
+    unsigned int guidesize;      /* -guidesize option value */
     unsigned int magnifypat;     /* -magnifypat option */
-    unsigned int xshift;         /* -xshift option */
-    unsigned int yshift;         /* -yshift option */
-    const char * patFilespec;    /* -patfile option.  Null if none */
+    int xshift;                  /* -xshift option */
+    int yshift;                  /* -yshift option */
+    const char * patfile;        /* -patfile option.  Null if none */
+    const char * texfile;        /* -texfile option.  Null if none */
+    const char * bgcolor;        /* -bgcolor option */
+    unsigned int smoothing;      /* -smoothing option */
     unsigned int randomseed;     /* -randomseed option */
+    unsigned int randomseedSpec; /* -randomseed option count */
     enum outputType outputType;  /* Type of output file */
+    unsigned int xbegin;         /* -xbegin option */
+    unsigned int xbeginSpec;     /* -xbegin option count */
 };
 
 
 
 static void
-parseCommandLine(int                 argc,
-                 char **             argv,
-                 struct cmdlineInfo *cmdlineP ) {
+parseNearFarPlanes(const char ** const nearFarPlanes,
+                   float *       const nearPlaneP,
+                   float *       const farPlaneP) {
+/*----------------------------------------------------------------------------
+  Parse nearFarPlanes option value into exactly two positive numbers
+-----------------------------------------------------------------------------*/
+    float nearPlane, farPlane;
+
+    if (nearFarPlanes == NULL || nearFarPlanes[0] == NULL ||
+        nearFarPlanes[1] == NULL || nearFarPlanes[2] != NULL)
+        pm_error("-planes requires exactly two positive numbers");
+
+    errno = 0;
+    nearPlane = strtof(nearFarPlanes[0], NULL);
+    if (errno != 0 || nearPlane <= 0.0)
+        pm_error("-planes requires exactly two positive numbers");
+
+    farPlane = strtof(nearFarPlanes[1], NULL);
+    if (errno != 0 || farPlane <= 0.0)
+        pm_error("-planes requires exactly two positive numbers");
+
+    if (nearPlane >= farPlane)
+        pm_error("-planes requires the near-plane value "
+                 "to be less than the far-plane value");
+
+    *nearPlaneP = nearPlane;
+    *farPlaneP  = farPlane;
+}
+
+
+
+static void
+parseCommandLine(int                  argc,
+                 const char **        argv,
+                 struct cmdlineInfo * cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.
@@ -101,17 +145,18 @@ parseCommandLine(int                 argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
-    unsigned int patfileSpec, dpiSpec, eyesepSpec, depthSpec,
-        guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, randomseedSpec;
+    unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec,
+        guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec,
+      bgcolorSpec, smoothingSpec, planesSpec;
 
     unsigned int blackandwhite, grayscale, color;
-
+    const char ** planes;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -136,27 +181,40 @@ parseCommandLine(int                 argc,
             &depthSpec,               0);
     OPTENT3(0, "maxval",          OPT_UINT,   &cmdlineP->maxval,
             &cmdlineP->maxvalSpec,    0);
-    OPTENT3(0, "guidesize",       OPT_INT,    &cmdlineP->guidesize,
+    OPTENT3(0, "guidetop",        OPT_FLAG,   NULL,
+            &cmdlineP->guidetop,      0);
+    OPTENT3(0, "guidebottom",     OPT_FLAG,   NULL,
+            &cmdlineP->guidebottom,   0);
+    OPTENT3(0, "guidesize",       OPT_UINT,   &cmdlineP->guidesize,
             &guidesizeSpec,           0);
     OPTENT3(0, "magnifypat",      OPT_UINT,   &cmdlineP->magnifypat,
             &magnifypatSpec,          0);
-    OPTENT3(0, "xshift",          OPT_UINT,   &cmdlineP->xshift,
+    OPTENT3(0, "xshift",          OPT_INT,    &cmdlineP->xshift,
             &xshiftSpec,              0);
-    OPTENT3(0, "yshift",          OPT_UINT,   &cmdlineP->yshift,
+    OPTENT3(0, "yshift",          OPT_INT,    &cmdlineP->yshift,
             &yshiftSpec,              0);
-    OPTENT3(0, "patfile",         OPT_STRING, &cmdlineP->patFilespec,
+    OPTENT3(0, "patfile",         OPT_STRING, &cmdlineP->patfile,
             &patfileSpec,             0);
+    OPTENT3(0, "texfile",         OPT_STRING, &cmdlineP->texfile,
+            &texfileSpec,             0);
+    OPTENT3(0, "bgcolor",         OPT_STRING, &cmdlineP->bgcolor,
+            &bgcolorSpec,             0);
     OPTENT3(0, "randomseed",      OPT_UINT,   &cmdlineP->randomseed,
-            &randomseedSpec,          0);
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "smoothing",       OPT_UINT,   &cmdlineP->smoothing,
+            &smoothingSpec,           0);
+    OPTENT3(0, "planes",          OPT_STRINGLIST, &planes,
+            &planesSpec,              0);
+    OPTENT3(0, "xbegin",          OPT_UINT,   &cmdlineP->xbegin,
+            &cmdlineP->xbeginSpec,    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 */
 
-    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 (blackandwhite + grayscale + color == 0)
         cmdlineP->outputType = OUTPUT_BW;
     else if (blackandwhite + grayscale + color > 1)
@@ -173,15 +231,21 @@ parseCommandLine(int                 argc,
         }
     }
     if (!patfileSpec)
-        cmdlineP->patFilespec = NULL;
+        cmdlineP->patfile = NULL;
+    if (!texfileSpec)
+        cmdlineP->texfile = NULL;
+    if (!bgcolorSpec)
+        cmdlineP->bgcolor = NULL;
+    if (!smoothingSpec)
+        cmdlineP->smoothing = 0;
 
     if (!dpiSpec)
-        cmdlineP->dpi = 96;
+        cmdlineP->dpi = 100;
     else if (cmdlineP->dpi < 1)
         pm_error("The argument to -dpi must be a positive integer");
 
     if (!eyesepSpec)
-        cmdlineP->eyesep = 2.5;
+        cmdlineP->eyesep = 2.56;
     else if (cmdlineP->eyesep <= 0.0)
         pm_error("The argument to -eyesep must be a positive number");
 
@@ -197,9 +261,16 @@ parseCommandLine(int                 argc,
             pm_error("-maxval must be at most %u.  You specified %u",
                      PNM_OVERALLMAXVAL, cmdlineP->maxval);
     }
+    if (bgcolorSpec && !texfileSpec)
+        pm_message("warning: -bgcolor has no effect "
+                   "except in conjunction with -texfile");
+
+    if (guidesizeSpec && !(cmdlineP->guidetop || cmdlineP->guidebottom))
+        pm_error("-guidesize has no meaning "
+                 "without -guidetop or -guidebottom");
 
     if (!guidesizeSpec)
-        cmdlineP->guidesize = 0;
+        cmdlineP->guidesize = 20;
 
     if (!magnifypatSpec)
         cmdlineP->magnifypat = 1;
@@ -212,26 +283,42 @@ parseCommandLine(int                 argc,
     if (!yshiftSpec)
         cmdlineP->yshift = 0;
 
-    if (!randomseedSpec)
-        cmdlineP->randomseed = time(NULL);
-
-    if (xshiftSpec && !cmdlineP->patFilespec)
+    if (xshiftSpec && !cmdlineP->patfile)
         pm_error("-xshift is valid only with -patfile");
-    if (yshiftSpec && !cmdlineP->patFilespec)
+    if (yshiftSpec && !cmdlineP->patfile)
         pm_error("-yshift is valid only with -patfile");
 
-    if (cmdlineP->makemask && cmdlineP->patFilespec)
+    if (cmdlineP->makemask && cmdlineP->patfile)
         pm_error("You may not specify both -makemask and -patfile");
 
-    if (cmdlineP->patFilespec && blackandwhite)
+    if (cmdlineP->patfile && blackandwhite)
         pm_error("-blackandwhite is not valid with -patfile");
-    if (cmdlineP->patFilespec && grayscale)
+    if (cmdlineP->patfile && grayscale)
         pm_error("-grayscale is not valid with -patfile");
-    if (cmdlineP->patFilespec && color)
+    if (cmdlineP->patfile && color)
         pm_error("-color is not valid with -patfile");
-    if (cmdlineP->patFilespec && cmdlineP->maxvalSpec)
+    if (cmdlineP->patfile && cmdlineP->maxvalSpec)
         pm_error("-maxval is not valid with -patfile");
 
+    if (cmdlineP->texfile && blackandwhite)
+        pm_error("-blackandwhite is not valid with -texfile");
+    if (cmdlineP->texfile && grayscale)
+        pm_error("-grayscale is not valid with -texfile");
+    if (cmdlineP->texfile && color)
+        pm_error("-color is not valid with -texfile");
+    if (cmdlineP->texfile && cmdlineP->maxvalSpec)
+        pm_error("-maxval is not valid with -texfile");
+    if (planesSpec && eyesepSpec)
+        pm_error("-planes is not valid with -eyesep");
+    if (planesSpec && depthSpec)
+        pm_error("-planes is not valid with -depth");
+
+    if (planesSpec) {
+        float nearPlane, farPlane;
+        parseNearFarPlanes(planes, &nearPlane, &farPlane);
+        cmdlineP->eyesep = 2.0*farPlane/cmdlineP->dpi;
+        cmdlineP->depth = 2.0*(farPlane-nearPlane) / (2.0*farPlane-nearPlane);
+    }
 
     if (argc-1 < 1)
         cmdlineP->inputFilespec = "-";
@@ -244,7 +331,7 @@ parseCommandLine(int                 argc,
 
 
 
-static int
+static unsigned int
 separation(double             const dist,
            double             const eyesep,
            unsigned int       const dpi,
@@ -254,9 +341,9 @@ separation(double             const dist,
   Return a separation in pixels which corresponds to a 3-D distance
   between the viewer's eyes and a point on an object.
 -----------------------------------------------------------------------------*/
-    int const pixelEyesep = round2int(eyesep * dpi);
+    unsigned int const pixelEyesep = ROUNDU(eyesep * dpi);
 
-    return round2int((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist));
+    return ROUNDU((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist));
 }
 
 
@@ -282,13 +369,27 @@ typedef tuple coord2Color(struct outGenerator *, int, int);
     /* A type to use for functions that map a 2-D coordinate to a color. */
 typedef void outGenStateTerm(struct outGenerator *);
 
+typedef struct {
+    struct pam   pam;
+    tuple **     imageData;
+    tuple        bgColor;
+    bool         replaceBgColor;
+        /* replace background color with pattern color */
+    unsigned int smoothing;
+        /* Number of background-smoothing iterations to perform */
+} texState;
 
 typedef struct outGenerator {
-    struct pam pam;
-    coord2Color * getTuple;
+    struct pam        pam;
+    coord2Color *     getTuple;
         /* Map from a height-map (x,y) coordinate to a tuple */
     outGenStateTerm * terminateState;
-    void * stateP;
+    void *            stateP;
+    texState *        textureP;
+        /* Mapped-texture part of the state of operation.  Null
+           means we are doing an ordinary stereogram instead of a
+           mapped-texture one.
+        */
 } outGenerator;
 
 
@@ -401,8 +502,8 @@ struct patternPixelState {
     /* This is the state of a patternPixel generator.*/
     struct pam   patPam;     /* Descriptor of pattern image */
     tuple **     patTuples;  /* Entire image read from the pattern file */
-    unsigned int xshift;
-    unsigned int yshift;
+    int xshift;
+    int yshift;
     unsigned int magnifypat;
 };
 
@@ -465,9 +566,9 @@ initPatternPixel(outGenerator *     const outGenP,
 
     MALLOCVAR_NOFAIL(stateP);
 
-    assert(cmdline.patFilespec);
-    
-    patternFileP = pm_openr(cmdline.patFilespec);
+    assert(cmdline.patfile);
+
+    patternFileP = pm_openr(cmdline.patfile);
 
     stateP->patTuples =
         pnm_readpam(patternFileP,
@@ -495,22 +596,92 @@ initPatternPixel(outGenerator *     const outGenP,
 
 
 static void
+readTextureImage(struct cmdlineInfo const cmdline,
+                 const struct pam * const inpamP,
+                 const struct pam * const outpamP,
+                 texState **        const texturePP) {
+
+    FILE *       textureFileP;
+    texState *   textureP;
+    struct pam * texPamP;
+
+    MALLOCVAR_NOFAIL(textureP);
+    texPamP = &textureP->pam;
+
+    textureFileP = pm_openr(cmdline.texfile);
+    textureP->imageData =
+        pnm_readpam(textureFileP, texPamP, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(textureFileP);
+
+    if (cmdline.bgcolor)
+        textureP->bgColor =
+            pnm_parsecolor(cmdline.bgcolor, texPamP->maxval);
+    else
+        textureP->bgColor =
+            pnm_backgroundtuple(texPamP, textureP->imageData);
+    textureP->replaceBgColor = (cmdline.patfile != NULL);
+    textureP->smoothing = cmdline.smoothing;
+
+    if (cmdline.verbose) {
+        const char * const colorname =
+            pnm_colorname(texPamP, textureP->bgColor, 1);
+
+        reportImageParameters("Texture file", texPamP);
+        if (cmdline.bgcolor && strcmp(colorname, cmdline.bgcolor))
+            pm_message("Texture background color: %s (%s)",
+                       cmdline.bgcolor, colorname);
+        else
+            pm_message("Texture background color: %s", colorname);
+        pm_strfree(colorname);
+    }
+
+    if (texPamP->width != inpamP->width || texPamP->height != inpamP->height)
+        pm_error("The texture image must have the same width and height "
+                 "as the input image");
+    if (cmdline.patfile &&
+        (!streq(texPamP->tuple_type, outpamP->tuple_type) ||
+         texPamP->maxval != outpamP->maxval))
+        pm_error("The texture image must be of the same tuple type "
+                 "and maxval as the pattern image");
+
+    textureP->pam.file = outpamP->file;
+
+    *texturePP = textureP;
+}
+
+
+
+static unsigned int
+totalGuideHeight(struct cmdlineInfo const cmdline) {
+
+    /* Each pair of guides is cmdline.guidesize high, and we add that much
+       white above and below as well, so the total vertical space is three
+       times cmdline.giudesize.
+    */
+
+    return
+        (cmdline.guidetop ? 3 * cmdline.guidesize : 0) +
+        (cmdline.guidebottom ? 3 * cmdline.guidesize : 0);
+}
+
+
+
+static void
 createoutputGenerator(struct cmdlineInfo const cmdline,
                       const struct pam * const inPamP,
                       outGenerator **    const outputGeneratorPP) {
 
     outGenerator * outGenP;
-    
+
     MALLOCVAR_NOFAIL(outGenP);
 
     outGenP->pam.size   = sizeof(struct pam);
     outGenP->pam.len    = PAM_STRUCT_SIZE(tuple_type);
     outGenP->pam.file   = stdout;
-    outGenP->pam.height = inPamP->height + 3 * abs(cmdline.guidesize);
-        /* Allow room for guides. */
+    outGenP->pam.height = inPamP->height + totalGuideHeight(cmdline);
     outGenP->pam.width  = inPamP->width;
 
-    if (cmdline.patFilespec) {
+    if (cmdline.patfile) {
         /* Background pixels should come from the pattern file. */
 
         initPatternPixel(outGenP, cmdline);
@@ -522,6 +693,12 @@ createoutputGenerator(struct cmdlineInfo const cmdline,
 
     outGenP->pam.bytes_per_sample = pnm_bytespersample(outGenP->pam.maxval);
 
+    if (cmdline.texfile) {
+        readTextureImage(cmdline, inPamP, &outGenP->pam, &outGenP->textureP);
+        outGenP->pam = outGenP->textureP->pam;
+    } else
+        outGenP->textureP = NULL;
+
     *outputGeneratorPP = outGenP;
 }
 
@@ -544,7 +721,7 @@ static void
 makeWhiteRow(const struct pam * const pamP,
              tuple *            const tuplerow) {
 
-    int col;
+    unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
         unsigned int plane;
@@ -561,144 +738,205 @@ writeRowCopies(const struct pam *  const outPamP,
                unsigned int        const copyCount) {
 
     unsigned int i;
+
     for (i = 0; i < copyCount; ++i)
         pnm_writepamrow(outPamP, outrow);
 }
 
 
 
-/* Draw a pair of guide boxes. */
 static void
-drawguides(int                const guidesize,
+writeWhiteRows(const struct pam * const outPamP,
+               unsigned int       const count) {
+
+    tuple * outrow;             /* One row of output data */
+
+    outrow = pnm_allocpamrow(outPamP);
+
+    makeWhiteRow(outPamP, outrow);
+
+    writeRowCopies(outPamP, outrow, count);
+
+    pnm_freerow(outrow);
+}
+
+
+
+static void
+drawguides(unsigned int       const guidesize,
            const struct pam * const outPamP,
            double             const eyesep,
            unsigned int       const dpi,
            double             const depthOfField) {
-
-    int const far = separation(0, eyesep, dpi, depthOfField);
+/*----------------------------------------------------------------------------
+   Draw a pair of guide boxes, left and right.
+-----------------------------------------------------------------------------*/
+    unsigned int const far = separation(0, eyesep, dpi, depthOfField);
         /* Space between the two guide boxes. */
-    int const width = outPamP->width;    /* Width of the output image */
+    unsigned int const width = outPamP->width;  /* Width of the output image */
 
-    tuple *outrow;             /* One row of output data */
+    tuple * outrow;             /* One row of output data */
     tuple blackTuple;
-    int col;
+    unsigned int col;
 
     pnm_createBlackTuple(outPamP, &blackTuple);
 
     outrow = pnm_allocpamrow(outPamP);
 
-    /* Leave some blank rows before the guides. */
+    /* Put some white rows before the guides */
+    writeWhiteRows(outPamP, guidesize);
+
+    /* Initialize the row buffer to white */
     makeWhiteRow(outPamP, outrow);
-    writeRowCopies(outPamP, outrow, guidesize);
 
-    /* Draw the guides. */
-    if ((width - far + guidesize)/2 < 0 ||
-        (width + far - guidesize)/2 >= width)
+    if (far > width + guidesize)
         pm_message("warning: the guide boxes are completely out of bounds "
-                   "at %d DPI", dpi);
-    else if ((width - far - guidesize)/2 < 0 ||
-             (width + far + guidesize)/2 >= width)
-        pm_message("warning: the guide boxes are partially out of bounds "
-                   "at %d DPI", dpi);
-
-    for (col = (width - far - guidesize)/2;
-         col < (width - far + guidesize)/2;
-         ++col)
-        if (col >= 0 && col < width)
-            pnm_assigntuple(outPamP, outrow[col], blackTuple);
+                   "at %u DPI", dpi);
+    else {
+        unsigned int leftBeg, leftEnd, rightBeg, rightEnd;
+
+        assert(far <= width + guidesize);
+        leftEnd  = (width - far + guidesize)/2;
+        assert(guidesize <= width + far);
+        rightBeg = (width + far - guidesize)/2;
+
+        if (far + guidesize > width) {
+            pm_message("warning: the guide boxes are partially out of bounds "
+                       "at %u DPI", dpi);
+
+            leftBeg  = 0;
+            rightEnd = width;
+        } else {
+            assert(far + guidesize <= width);
+            leftBeg  = (width - far - guidesize)/2;
+            rightEnd = (width + far + guidesize)/2;
+        }
 
-    for (col = (width + far - guidesize)/2;
-         col < (width + far + guidesize)/2;
-         ++col)
-        if (col >= 0 && col < width)
+        /* Draw the left guide black in the buffer */
+        assert(leftEnd < outPamP->width);
+        for (col = leftBeg; col < leftEnd; ++col)
             pnm_assigntuple(outPamP, outrow[col], blackTuple);
 
-    writeRowCopies(outPamP,outrow, guidesize);
+        /* Draw the right guide black in the buffer */
+        assert(rightEnd <= outPamP->width);
+        for (col = rightBeg; col < rightEnd; ++col)
+            pnm_assigntuple(outPamP, outrow[col], blackTuple);
+    }
+    /* Write out the guide rows */
 
-    /* Leave some blank rows after the guides. */
-    makeWhiteRow(outPamP, outrow);
     writeRowCopies(outPamP, outrow, guidesize);
 
+    /* Put some white rows after the guides */
+    writeWhiteRows(outPamP, guidesize);
+
     pnm_freerow(outrow);
 }
 
 
 
-/* Do the bulk of the work.  See the paper cited above for code
- * comments.  All I (Scott) did was transcribe the code and make
- * minimal changes for Netpbm.  And some style changes by Bryan to
- * match Netpbm style.
- */
 static void
 makeStereoRow(const struct pam * const inPamP,
               tuple *            const inRow,
-              int *              const same,
+              unsigned int *     const sameL,
+              unsigned int *     const sameR,
               double             const depthOfField,
               double             const eyesep,
-              unsigned int       const dpi) {
+              unsigned int       const dpi,
+              unsigned int       const optWidth,
+              unsigned int       const smoothing) {
 
-#define Z(X) (1.0-inRow[X][0]/(double)inPamP->maxval)
+/* Given a row of the depth map, compute the sameL and sameR arrays,
+ * which indicate for each pixel which pixel to its left and right it
+ * should be colored the same as.
+ */
+#define Z(X) (inRow[X][0]/(double)inPamP->maxval)
 
-    int const width       = inPamP->width;
-    int const pixelEyesep = round2int(eyesep * dpi);
-        /* Separation in pixels between the viewer's eyes */
+    unsigned int col;
 
-    int col;
+    for (col = 0; col < inPamP->width; ++col) {
+        sameL[col] = col;
+        sameR[col] = col;
+    }
 
-    for (col = 0; col < width; ++col)
-        same[col] = col;
-
-    for (col = 0; col < width; ++col) {
-        int const s = separation(Z(col), eyesep, dpi, depthOfField);
-        int left, right;
-
-        left  = col - s/2;  /* initial value */
-        right = left + s;   /* initial value */
-
-        if (0 <= left && right < width) {
-            int visible;
-            int t;
-            double zt;
-
-            t = 1;  /* initial value */
-
-            do {
-                double const dof = depthOfField;
-                zt = Z(col) + 2.0*(2.0 - dof*Z(col))*t/(dof*pixelEyesep);
-                visible = Z(col-t) < zt && Z(col+t) < zt;
-                ++t;
-            } while (visible && zt < 1);
-            if (visible) {
-                int l;
-
-                l = same[left];
-                while (l != left && l != right) {
-                    if (l < right) {
-                        left = l;
-                        l = same[left];
-                    } else {
-                        same[left] = right;
-                        left = right;
-                        l = same[left];
-                        right = l;
-                    }
-                }
-                same[left] = right;
+    for (col = 0; col < inPamP->width; ++col) {
+        unsigned int const sep = separation(Z(col), eyesep, dpi, depthOfField);
+        int const left = col - sep/2;
+        int const right = left + sep;
+
+        if (left >= 0 && right < inPamP->width) {
+            bool isVisible;
+        
+            if (sameL[right] != right) {
+                /* Right point already linked */
+                if (sameL[right] < left) {
+                    /* Deeper than current */
+                    sameR[sameL[right]] = sameL[right];  /* Break old links. */
+                    sameL[right] = right;
+                    isVisible = TRUE;
+                } else
+                    isVisible = FALSE;
+            } else
+                isVisible = TRUE;
+
+            if (sameR[left] != left) {
+                /* Left point already linked */
+                if (sameR[left] > right) {
+                    /* Deeper than current */
+                    sameL[sameR[left]] = sameR[left];  /* Break old links. */
+                    sameR[left] = left;
+                    isVisible = TRUE;
+                } else
+                    isVisible = FALSE;
+            } else
+                isVisible = TRUE;
+
+            if (isVisible) {
+                /* Make a link. */
+                sameL[right] = left;
+                sameR[left] = right;
             }
         }
     }
+
+    /* If smoothing is enabled, replace each non-duplicate pixel with
+       the pixel adjacent to its right neighbor.
+    */
+    if (smoothing > 0) {
+        int const baseCol = inPamP->width - optWidth - 1;
+
+        int col;
+
+        for (col = inPamP->width - 1; col >= 0; --col)
+            sameR[col] = sameR[sameR[col]];
+        for (col = baseCol; col >= 0; --col) {
+            if (sameR[col] == col)
+                sameR[col] = sameR[col+1] - 1;
+        }
+    }
 }
 
 
 
 static void
-makeMaskRow(const struct pam * const outPamP,
-            const int *        const same,
-            const tuple *      const outRow) {
+makeMaskRow(const struct pam *   const outPamP,
+            unsigned int         const xbegin,
+            const unsigned int * const sameL,
+            const unsigned int * const sameR,
+            const tuple *        const outRow) {
     int col;
 
-    for (col = outPamP->width-1; col >= 0; --col) {
-        bool const duplicate = (same[col] != col);
+    for (col = (int)xbegin; col < outPamP->width; ++col) {
+        bool const duplicate = (sameL[col] != col && sameL[col] >= xbegin);
+
+        unsigned int plane;
+
+        for (plane = 0; plane < outPamP->depth; ++plane)
+            outRow[col][plane] = duplicate ? outPamP->maxval : 0;
+    }
+
+    for (col = (int)xbegin - 1; col >= 0; --col) {
+        bool const duplicate = (sameR[col] != col);
+
         unsigned int plane;
 
         for (plane = 0; plane < outPamP->depth; ++plane)
@@ -709,37 +947,284 @@ makeMaskRow(const struct pam * const outPamP,
 
 
 static void
-makeImageRow(outGenerator * const outGenP,
-             int            const row,
-             const int *    const same,
-             const tuple *  const outRow) {
+computeFixedPoint(const unsigned int * const same,
+                  unsigned int *       const sameFp,
+                  unsigned int         const width) {
 /*----------------------------------------------------------------------------
-  same[N] is one of two things:
+  Compute the fixed point of same[] (i.e., sameFp[x] is
+  same[same[same[...[same[x]]...]]]).
+-----------------------------------------------------------------------------*/
+    int col;
 
-  same[N] == N means to generate a value for Column N independent of
+    for (col = width-1; col >= 0; --col) {
+        if (same[col] != col)
+            sameFp[col] = sameFp[same[col]];
+        else {
+            if (col < width-1)
+                sameFp[col] = sameFp[col + 1] - 1;
+            else
+                sameFp[col] = col;
+        }
+    }
+}
+
+
+
+static void
+averageFromPattern(struct pam *         const pamP,
+                   tuple                const bgColor,
+                   const tuple *        const textureRow,
+                   const unsigned int * const same,
+                   unsigned int *       const sameFp,
+                   const tuple *        const outRow,
+                   unsigned int *       const tuplesInCol) {
+/*----------------------------------------------------------------------------
+  Average the color of each non-background pattern tuple to every column that
+  should have the same color.
+-----------------------------------------------------------------------------*/
+    int col;
+
+    /* Initialize the tuple sums to zero. */
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane)
+            outRow[col][plane] = 0;
+        tuplesInCol[col] = 0;
+    }
+
+    /* Accumulate the color of each non-background pattern tuple to
+       every column that should have the same color.
+    */
+    for (col = pamP->width-1; col >= 0; --col) {
+        tuple const onetuple = textureRow[(col+same[col])/2];
+        unsigned int const targetcol = sameFp[col];
+        int eqcol;
+
+        if (!pnm_tupleequal(pamP, onetuple, bgColor)) {
+            for (eqcol = pamP->width-1; eqcol >= 0; --eqcol) {
+                if (sameFp[eqcol] == targetcol) {
+                    unsigned int plane;
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        outRow[eqcol][plane] += onetuple[plane];
+                    tuplesInCol[eqcol]++;
+                }
+            }
+        }
+    }
+    /* Take the average of all colors associated with each column.
+       Tuples that can be any color are assigned the same color as was
+       previously assigned to their fixed-point column.
+    */
+    for (col = 0; col < pamP->width; ++col) {
+        if (tuplesInCol[col] > 0) {
+            unsigned int plane;
+            for (plane = 0; plane < pamP->depth; ++plane)
+                outRow[col][plane] /= tuplesInCol[col];
+        } else
+            pnm_assigntuple(pamP, outRow[col], bgColor);
+    }
+}
+
+
+
+static void
+smoothOutSpeckles(struct pam *   const pamP,
+                  tuple          const bgColor,
+                  unsigned int   const smoothing,
+                  unsigned int * const tuplesInCol,
+                  tuple *        const rowBuffer,
+                  const tuple *  const outRow) {
+/*----------------------------------------------------------------------------
+  Smooth out small speckles of the background color lying between other
+  colors.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < smoothing; ++i) {
+        int col;
+        tuple * const scratchrow = rowBuffer;
+        for (col = pamP->width-2; col >= 1; --col) {
+            if (tuplesInCol[col] == 0) {
+                /* Replace a background tuple with the average of its
+                   left and right neighbors.
+                */
+                unsigned int plane;
+                for (plane = 0; plane < pamP->depth; ++plane)
+                    scratchrow[col][plane] = 0;
+                if (!pnm_tupleequal(pamP, outRow[col-1], bgColor)) {
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        scratchrow[col][plane] += outRow[col-1][plane];
+                    ++tuplesInCol[col];
+                }
+                if (!pnm_tupleequal(pamP, outRow[col+1], bgColor)) {
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        scratchrow[col][plane] += outRow[col+1][plane];
+                    ++tuplesInCol[col];
+                }
+                if (tuplesInCol[col] > 0)
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        scratchrow[col][plane] /= tuplesInCol[col];
+                else
+                    pnm_assigntuple(pamP, scratchrow[col], outRow[col]);
+            } else
+                pnm_assigntuple(pamP, scratchrow[col], outRow[col]);
+        }
+        for (col = 1; col < pamP->width-1; ++col)
+            pnm_assigntuple(pamP, outRow[col], scratchrow[col]);
+    }
+}
+
+
+
+static void
+replaceRemainingBackgroundWithPattern(outGenerator *       const outGenP,
+                                      const unsigned int * const same,
+                                      unsigned int         const row,
+                                      const tuple *        const outRow) {
+
+    const struct pam * const pamP = &outGenP->pam;
+    tuple const bgColor = outGenP->textureP->bgColor;
+
+    if (outGenP->textureP->replaceBgColor) {
+        int col;
+        for (col = outGenP->pam.width-1; col >= 0; --col) {
+            if (pnm_tupleequal(pamP, outRow[col], bgColor)) {
+                bool const duplicate = (same[col] != col);
+
+                tuple newtuple;
+
+                if (duplicate) {
+                    assert(same[col] > col);
+                    assert(same[col] < outGenP->pam.width);
+
+                    newtuple = outRow[same[col]];
+                } else
+                    newtuple = outGenP->getTuple(outGenP, col, row);
+
+                pnm_assigntuple(pamP, outRow[col], newtuple);
+            }
+        }
+    }
+}
+
+
+
+static void
+makeImageRowMts(outGenerator *       const outGenP,
+                unsigned int         const row,
+                const unsigned int * const same,
+                unsigned int *       const sameFp,
+                tuple *              const rowBuffer,
+                const tuple *        const outRow) {
+/*----------------------------------------------------------------------------
+  Make a row of a mapped-texture stereogram.
+-----------------------------------------------------------------------------*/
+    unsigned int * tuplesInCol;
+        /* tuplesInCol[C] is the number of tuples averaged together to make
+           Column C.
+        */
+    MALLOCARRAY(tuplesInCol, outGenP->pam.width);
+    if (tuplesInCol == NULL)
+        pm_error("Unable to allocate space for \"tuplesInCol\" array.");
+
+    assert(outGenP->textureP);
+    /* This is an original algorithm by Scott Pakin. */
+
+    /*
+      Compute the fixed point of same[] (i.e., sameFp[x] is
+      same[same[same[...[same[x]]...]]]).
+    */
+    computeFixedPoint(same, sameFp, outGenP->pam.width);
+
+    /* Average the color of each non-background pattern tuple to
+       every column that should have the same color.
+    */
+
+    averageFromPattern(&outGenP->pam, outGenP->textureP->bgColor,
+                       outGenP->textureP->imageData[row],
+                       same, sameFp,
+                       outRow, tuplesInCol);
+
+    /* Smooth out small speckles of the background color lying between
+       other colors.
+    */
+    smoothOutSpeckles(&outGenP->pam, outGenP->textureP->bgColor,
+                      outGenP->textureP->smoothing,
+                      tuplesInCol, rowBuffer, outRow);
+
+    /* Replace any remaining background tuples with a pattern tuple. */
+
+    replaceRemainingBackgroundWithPattern(outGenP, same, row, outRow);
+
+    free(tuplesInCol);
+}
+
+
+
+static void
+makeImageRow(outGenerator *       const outGenP,
+             unsigned int         const row,
+             unsigned int         const optWidth,
+             unsigned int         const xbegin,
+             const unsigned int * const sameL,
+             const unsigned int * const sameR,
+             const tuple *        const outRow) {
+/*----------------------------------------------------------------------------
+  sameR[N] is one of two things:
+
+  sameR[N] == N means to generate a value for Column N independent of
   other columns in the row.
 
-  same[N] > N means Column N should be identical to Column same[N].
-  
-  same[N] < N is not allowed.
+  sameR[N] > N means Column N should be identical to Column sameR[N].
+
+  sameR[N] < N is not allowed.
+
+  sameL[N] is one of two things:
+
+  sameL[N] == N means to generate a value for Column N independent of
+  other columns in the row.
+
+  sameL[N] < N means Column N should be identical to Column sameL[N].
+
+  sameL[N] > N is not allowed.
 -----------------------------------------------------------------------------*/
     int col;
-    for (col = outGenP->pam.width-1; col >= 0; --col) {
-        bool const duplicate = (same[col] != col);
-        
+    int lastLinked;
+
+    for (col = (int)xbegin, lastLinked = INT_MIN;
+         col < outGenP->pam.width;
+         ++col) {
+
         tuple newtuple;
-        unsigned int plane;
 
-        if (duplicate) {
-            assert(same[col] > col);
-            assert(same[col] < outGenP->pam.width);
+        if (sameL[col] == col || sameL[col] < (int)xbegin) {
+            if (lastLinked == col - 1)
+                newtuple = outRow[col - 1];
+            else
+                newtuple = outGenP->getTuple(outGenP, col, row);
+        } else {
+          newtuple = outRow[sameL[col]];
+          lastLinked = col;
+              /* Keep track of the last pixel to be constrained. */
+        }
+        pnm_assigntuple(&outGenP->pam, outRow[col], newtuple);
+    }
 
-            newtuple = outRow[same[col]];
-        } else 
-            newtuple = outGenP->getTuple(outGenP, col, row);
+    for (col = (int)xbegin - 1, lastLinked = INT_MIN; col >= 0; --col) {
+        tuple newtuple;
 
-        for (plane = 0; plane < outGenP->pam.depth; ++plane)
-            outRow[col][plane] = newtuple[plane];
+        if (sameR[col] == col) {
+            if (lastLinked == col + 1)
+                newtuple = outRow[col + 1];
+            else
+                newtuple = outGenP->getTuple(outGenP, col, row);
+        } else {
+            newtuple = outRow[sameR[col]];
+            lastLinked = col;
+                /* Keep track of the last pixel to be constrained. */
+        }
+        pnm_assigntuple(&outGenP->pam, outRow[col], newtuple);
     }
 }
 
@@ -764,26 +1249,40 @@ makeImageRows(const struct pam * const inPamP,
               double             const eyesep,
               unsigned int       const dpi,
               bool               const crossEyed,
-              bool               const makeMask) {
+              bool               const makeMask,
+              unsigned int       const magnifypat,
+              unsigned int       const smoothing,
+              unsigned int       const xbegin) {
 
     tuple * inRow;     /* One row of pixels read from the height-map file */
     tuple * outRow;    /* One row of pixels to write to the height-map file */
-    int * same;
-        /* Array: same[N] is the column number of a pixel to the right forced
-           to have the same color as the one in column N
+    unsigned int * sameR;
+        /* Malloced array: sameR[N] is the column number of a pixel to the
+           right forced to have the same color as the one in column N
+        */
+    unsigned int * sameL;
+        /* Malloced array: sameL[N] is the column number of a pixel to the
+           left forced to have the same color as the one in column N
         */
-    int row;           /* Current row in the input and output files */
+    unsigned int * sameRfp;
+        /* Malloced array: Fixed point of sameR[] */
+    tuple * rowBuffer;     /* Scratch row needed for texture manipulation */
+    unsigned int row;      /* Current row in the input and output files */
 
     inRow = pnm_allocpamrow(inPamP);
     outRow = pnm_allocpamrow(&outputGeneratorP->pam);
-    MALLOCARRAY(same, inPamP->width);
-    if (same == NULL)
-        pm_error("Unable to allocate space for \"same\" array.");
-
-    /* See the paper cited above for code comments.  All I (Scott) did was
-     * transcribe the code and make minimal changes for Netpbm.  And some
-     * style changes by Bryan to match Netpbm style.
-     */
+    MALLOCARRAY(sameR, inPamP->width);
+    if (sameR == NULL)
+        pm_error("Unable to allocate space for \"sameR\" array.");
+    MALLOCARRAY(sameL, inPamP->width);
+    if (sameL == NULL)
+        pm_error("Unable to allocate space for \"sameL\" array.");
+
+    MALLOCARRAY(sameRfp, inPamP->width);
+    if (sameRfp == NULL)
+        pm_error("Unable to allocate space for \"sameRfp\" array.");
+    rowBuffer = pnm_allocpamrow(&outputGeneratorP->pam);
+
     for (row = 0; row < inPamP->height; ++row) {
         pnm_readpamrow(inPamP, inRow);
         if (crossEyed)
@@ -793,17 +1292,29 @@ makeImageRows(const struct pam * const inPamP,
             invertHeightRow(inPamP, inRow);
 
         /* Determine color constraints. */
-        makeStereoRow(inPamP, inRow, same, depthOfField, eyesep, dpi);
+        makeStereoRow(inPamP, inRow, sameL, sameR, depthOfField, eyesep, dpi,
+                      ROUNDU(eyesep * dpi)/(magnifypat * 2),
+                      smoothing);
 
         if (makeMask)
-            makeMaskRow(&outputGeneratorP->pam, same, outRow);
-        else
-            makeImageRow(outputGeneratorP, row, same, outRow);
-
+            makeMaskRow(&outputGeneratorP->pam, xbegin, sameL, sameR, outRow);
+        else {
+            if (outputGeneratorP->textureP)
+                makeImageRowMts(outputGeneratorP, row, sameR, sameRfp,
+                                rowBuffer, outRow);
+            else
+                makeImageRow(outputGeneratorP, row,
+                             ROUNDU(eyesep * dpi)/(magnifypat * 2),
+                             xbegin, sameL, sameR, outRow);
+        }
         /* Write the resulting row. */
         pnm_writepamrow(&outputGeneratorP->pam, outRow);
     }
-    free(same);
+
+    pnm_freepamrow(rowBuffer);
+    free(sameRfp);
+    free(sameL);
+    free(sameR);
     pnm_freepamrow(outRow);
     pnm_freepamrow(inRow);
 }
@@ -817,7 +1328,9 @@ produceStereogram(FILE *             const ifP,
     struct pam inPam;    /* PAM information for the height-map file */
     outGenerator * outputGeneratorP;
         /* Handle of an object that generates background pixels */
-    
+    unsigned int xbegin;
+        /* x coordinate separating left-to-right from right-to-left coloring */
+
     pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type));
 
     createoutputGenerator(cmdline, &inPam, &outputGeneratorP);
@@ -832,21 +1345,34 @@ produceStereogram(FILE *             const ifP,
 
     pnm_writepaminit(&outputGeneratorP->pam);
 
-    /* Draw guide boxes at the top, if desired. */
-    if (cmdline.guidesize < 0)
-        drawguides(-cmdline.guidesize, &outputGeneratorP->pam,
+    if (cmdline.xbeginSpec == 0)
+        xbegin = outputGeneratorP->pam.width/2;
+    else {
+        xbegin = cmdline.xbegin;
+        if (xbegin >= outputGeneratorP->pam.width)
+            pm_error("-xbegin must be less than the image width (%d)",
+                     outputGeneratorP->pam.width);
+    }
+
+    if (cmdline.guidetop)
+        drawguides(cmdline.guidesize, &outputGeneratorP->pam,
                    cmdline.eyesep,
                    cmdline.dpi, cmdline.depth);
 
     makeImageRows(&inPam, outputGeneratorP,
                   cmdline.depth, cmdline.eyesep, cmdline.dpi,
-                  cmdline.crosseyed, cmdline.makemask);
+                  cmdline.crosseyed, cmdline.makemask, cmdline.magnifypat,
+                  cmdline.smoothing, xbegin);
 
-    /* Draw guide boxes at the bottom, if desired. */
-    if (cmdline.guidesize > 0)
+    if (cmdline.guidebottom)
         drawguides(cmdline.guidesize, &outputGeneratorP->pam,
                    cmdline.eyesep, cmdline.dpi, cmdline.depth);
 
+    if (cmdline.texfile) {
+        pnm_freepamarray(outputGeneratorP->textureP->imageData,
+                         &outputGeneratorP->textureP->pam);
+        free(outputGeneratorP->textureP);
+    }
     destroyoutputGenerator(outputGeneratorP);
 }
 
@@ -855,7 +1381,12 @@ produceStereogram(FILE *             const ifP,
 static void
 reportParameters(struct cmdlineInfo const cmdline) {
 
-    unsigned int const pixelEyesep = round2int(cmdline.eyesep * cmdline.dpi);
+    unsigned int const pixelEyesep =
+        ROUNDU(cmdline.eyesep * cmdline.dpi);
+    unsigned int const sep0 =
+        separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth);
+    unsigned int const sep1 =
+        separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth);
 
     pm_message("Eye separation: %.4g inch * %d DPI = %u pixels",
                cmdline.eyesep, cmdline.dpi, pixelEyesep);
@@ -863,40 +1394,42 @@ reportParameters(struct cmdlineInfo const cmdline) {
     if (cmdline.magnifypat > 1)
         pm_message("Background magnification: %uX * %uX",
                    cmdline.magnifypat, cmdline.magnifypat);
-    pm_message("\"Optimal\" pattern width: %u / (%u * 2) = %u pixels",
+    pm_message("\"Optimal\" (far) pattern width: %u / (%u * 2) = %u pixels",
                pixelEyesep, cmdline.magnifypat,
                pixelEyesep/(cmdline.magnifypat * 2));
-    pm_message("Unique 3-D depth levels possible: %u",
-               separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth) -
-               separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth) + 1);
-    if (cmdline.patFilespec && (cmdline.xshift || cmdline.yshift))
-        pm_message("Pattern shift: (%u, %u)", cmdline.xshift, cmdline.yshift);
+    pm_message("Near pattern width: %u / %u = %u pixels",
+               sep1, cmdline.magnifypat, sep1 / cmdline.magnifypat);
+    pm_message("Unique 3-D depth levels possible: %u", sep0 - sep1 + 1);
+    if (cmdline.patfile && (cmdline.xshift || cmdline.yshift))
+        pm_message("Pattern shift: (%d, %d)", cmdline.xshift, cmdline.yshift);
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;      /* Parsed command line */
     FILE * ifP;
-    
-    /* Parse the command line. */
-    pnm_init(&argc, argv);
+
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
+
     if (cmdline.verbose)
         reportParameters(cmdline);
-    
-    srand(cmdline.randomseed);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
     ifP = pm_openr(cmdline.inputFilespec);
-        
+
     /* Produce a stereogram. */
     produceStereogram(ifP, cmdline);
 
     pm_close(ifP);
-    
+
     return 0;
 }
+
+
+
diff --git a/generator/pbmmake.c b/generator/pbmmake.c
index 41d80274..600440f0 100644
--- a/generator/pbmmake.c
+++ b/generator/pbmmake.c
@@ -37,7 +37,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -56,7 +56,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (blackOpt + whiteOpt + grayOpt > 1)
@@ -101,13 +101,11 @@ writeGrayRaster(unsigned int const cols,
         bitrow1[i] = (PBM_WHITE*0x55) | (PBM_BLACK*0xaa);
         /* 0xaa = 10101010 ; 0x55 = 01010101 */
     }
-    if (cols % 8 > 0) { 
-        bitrow0[lastCol] >>= 8 - cols % 8;
-        bitrow0[lastCol] <<= 8 - cols % 8;
-        bitrow1[lastCol] >>= 8 - cols % 8;
-        bitrow1[lastCol] <<= 8 - cols % 8;
-    }
-    if (rows > 1) {
+
+    pbm_cleanrowend_packed(bitrow0, cols);
+    pbm_cleanrowend_packed(bitrow1, cols);
+
+  if (rows > 1) {
         unsigned int row;
         for (row = 1; row < rows; row += 2) {
             pbm_writepbmrow_packed(ofP, bitrow0, cols, 0);
@@ -139,11 +137,10 @@ writeSingleColorRaster(unsigned int const cols,
     for (i = 0; i <= lastCol; ++i) 
         bitrow0[i] = color*0xff;
 
-    if (cols % 8 > 0) { 
-        bitrow0[lastCol] >>= 8 - cols % 8;
-        bitrow0[lastCol] <<= 8 - cols % 8;
-        /* row end trimming, really not necessary with white */
-    }
+    if (color != 0)
+        pbm_cleanrowend_packed(bitrow0, cols);
+    /* row end trimming, not necessary with white */
+
     {
         unsigned int row;
         for (row = 0; row < rows; ++row)
diff --git a/generator/pbmmake.test b/generator/pbmmake.test
deleted file mode 100644
index 0fd99ccd..00000000
--- a/generator/pbmmake.test
+++ /dev/null
@@ -1,9 +0,0 @@
-echo Test 1.  Should print 3892756435 12
-./pbmmake -white 16 2 | cksum
-echo Test 2.  Should print 1576602925 8
-./pbmmake -black 1 1  | cksum
-echo Test 3.  Should print 4272952448 14
-./pbmmake -gray 7 7   | cksum
-echo Test 4.  Should print 1634688086 15
-./pbmmake -grey 8 8   | cksum
-echo Tests done.
diff --git a/generator/pbmpage.c b/generator/pbmpage.c
index fcf7af42..a2f47bcc 100644
--- a/generator/pbmpage.c
+++ b/generator/pbmpage.c
@@ -17,6 +17,7 @@
 #include <math.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pbm.h"
 
 /* US is 8.5 in by 11 in */
@@ -31,10 +32,9 @@
 
 
 struct bitmap {
-    int Width;      /* width and height in 600ths of an inch */
-    int Height;
-    int Pwidth;     /* width in bytes */
-    char *bitmap;
+    unsigned int Width;      /* width and height in 600ths of an inch */
+    unsigned int Height;
+    bit ** bitmap;
 };
 
 static struct bitmap bitmap;
@@ -42,11 +42,10 @@ static struct bitmap bitmap;
 
 
 static void
-setpixel(int const x,
-         int const y,
-         int const c) {
+setpixel(unsigned int const x,
+         unsigned int const y,
+         unsigned int const c) {
 
-    int const charidx = y * bitmap.Pwidth + x/8;
     char const bitmask = 128 >> (x % 8);
 
     if (x < 0 || x >= bitmap.Width)
@@ -55,151 +54,148 @@ setpixel(int const x,
         return;
 
     if (c)
-        bitmap.bitmap[charidx] |= bitmask;
+        bitmap.bitmap[y][x/8] |= bitmask;
     else
-        bitmap.bitmap[charidx] &= ~bitmask;
+        bitmap.bitmap[y][x/8] &= ~bitmask;
 }
 
 
 
 static void 
-setplus(int x,int y,int s)
+setplus(unsigned int const x,
+        unsigned int const y,
+        unsigned int const s) {
 /*----------------------------------------------------------------------------
    Draw a black plus sign centered at (x,y) with arms 's' pixels long.  
    Leave the exact center of the plus white.
 -----------------------------------------------------------------------------*/
-{
-  int i;
-
-  for(i=0; i<s; i++)
-  {
-    setpixel(x+i,y,1);
-    setpixel(x-i,y,1);
-    setpixel(x,y+i,1);
-    setpixel(x,y-i,1);
-  }
+    unsigned int i;
+
+    for (i = 0; i < s; ++i) {
+        setpixel(x+i, y,   1);
+        setpixel(x-i, y,   1);
+        setpixel(x,   y+i, 1);
+        setpixel(x,   y-i, 1);
+    }
 }
 
 
 
 static void 
-setblock(int x,int y,int s)
-{
-  int i,j;
+setblock(unsigned int const x,
+         unsigned int const y,
+         unsigned int const s) {
+
+    unsigned int i;
+
+    for (i = 0; i < s; ++i) {
+        unsigned int j;
 
-  for(i=0; i<s; i++)
-    for(j=0; j<s; j++)
-      setpixel(x+i,y+j,1);
+        for (j = 0; j < s; ++j)
+            setpixel(x+i, y+j, 1);
+    }
 }
 
 
 
 static void 
-setchar(int x,int y,char c)
-{
-  int xo,yo;
-  static char charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e },
-				{ 0x00, 0x42, 0x7f, 0x40, 0x00 },
-				{ 0x42, 0x61, 0x51, 0x49, 0x46 },
-				{ 0x22, 0x41, 0x49, 0x49, 0x36 },
-				{ 0x18, 0x14, 0x12, 0x7f, 0x10 },
-				{ 0x27, 0x45, 0x45, 0x45, 0x39 },
-				{ 0x3e, 0x49, 0x49, 0x49, 0x32 },
-				{ 0x01, 0x01, 0x61, 0x19, 0x07 },
-				{ 0x36, 0x49, 0x49, 0x49, 0x36 },
-				{ 0x26, 0x49, 0x49, 0x49, 0x3e } };
-
-  if(c<='9' && c>='0')
-    for(xo=0; xo<5; xo++)
-      for(yo=0; yo<8; yo++)
-	if((charmap[c-'0'][xo]>>yo)&1)
-	  setblock(x+xo*3,y+yo*3,3);
+setchar(unsigned int const x,
+        unsigned int const y,
+        char         const c) {
+
+    static char const charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e },
+                                        { 0x00, 0x42, 0x7f, 0x40, 0x00 },
+                                        { 0x42, 0x61, 0x51, 0x49, 0x46 },
+                                        { 0x22, 0x41, 0x49, 0x49, 0x36 },
+                                        { 0x18, 0x14, 0x12, 0x7f, 0x10 },
+                                        { 0x27, 0x45, 0x45, 0x45, 0x39 },
+                                        { 0x3e, 0x49, 0x49, 0x49, 0x32 },
+                                        { 0x01, 0x01, 0x61, 0x19, 0x07 },
+                                        { 0x36, 0x49, 0x49, 0x49, 0x36 },
+                                        { 0x26, 0x49, 0x49, 0x49, 0x3e } };
+    
+    if (c <= '9' && c >= '0') {
+        unsigned int xo;
+
+        for (xo = 0; xo < 5; ++xo) {
+            unsigned int yo;
+
+            for (yo = 0; yo < 8; ++yo) {
+                if ((charmap[c-'0'][xo] >> yo) & 0x01)
+                    setblock(x + xo*3, y + yo*3, 3);
+            }
+        }
+    }
 }
 
 
 
 static void 
-setstring(int x,int y,char* s)
-{
-  char* p;
-  int xo;
+setstring(unsigned int const x,
+          unsigned int const y,
+          const char * const s) {
 
-  for(xo=0, p=s; *p; xo+=21, p++)
-    setchar(x+xo,y,*p);
+    const char * p;
+    unsigned int xo;
+
+    for (xo = 0, p = s; *p; xo += 21, ++p)
+        setchar(x + xo, y, *p);
 }
 
 
 
 static void 
-setCG(int x,int y)
-{
-  int xo,yo,zo;
-
-  for(xo=0; xo<=50; xo++)
-  {
-    yo=sqrt(50.0*50.0-xo*xo);
-    setpixel(x+xo,y+yo,1);
-    setpixel(x+yo,y+xo,1);
-    setpixel(x-1-xo,y-1-yo,1);
-    setpixel(x-1-yo,y-1-xo,1);
-    setpixel(x+xo,y-1-yo,1);
-    setpixel(x-1-xo,y+yo,1);
-    for(zo=0; zo<yo; zo++)
-    {
-      setpixel(x+xo,y-1-zo,1);
-      setpixel(x-1-xo,y+zo,1);
+setCG(unsigned int const x,
+      unsigned int const y) {
+
+    unsigned int xo;
+
+    for (xo = 0; xo <= 50; ++xo)   {
+        unsigned int const yo = sqrt(SQR(50.0) - SQR(xo));
+
+        unsigned int zo;
+
+        setpixel(x + xo, y + yo, 1);
+        setpixel(x+yo,   y + xo, 1);
+        setpixel(x-1-xo, y-1-yo, 1);
+        setpixel(x-1-yo, y-1-xo, 1);
+        setpixel(x+xo,   y-1-yo, 1);
+        setpixel(x-1-xo, y+yo,   1);
+
+        for(zo = 0; zo < yo; ++zo) {
+            setpixel(x + xo, y-1-zo, 1);
+            setpixel(x-1-xo, y+zo,   1);
+        }
     }
-  }
 }
 
 
 
 static void
-outputPbm(FILE *        const file,
+outputPbm(FILE *        const ofP,
           struct bitmap const bitmap) {
 /*----------------------------------------------------------------------------
   Create a pbm file containing the image from the global variable bitmap[].
 -----------------------------------------------------------------------------*/
     int const forceplain = 0;
-    bit *pbmrow;
-    int row;
-    int bitmap_cursor;
+
+    unsigned int row;
     
-    pbm_writepbminit(file, bitmap.Width, bitmap.Height, forceplain);
-  
-    /* We round the allocated row space up to a multiple of 8 so the ugly
-       fast code below can work.
-       */
-    pbmrow = pbm_allocrow(((bitmap.Width+7)/8)*8);
+    pbm_writepbminit(ofP, bitmap.Width, bitmap.Height, forceplain);
     
-    bitmap_cursor = 0;
-    for (row = 0; row < bitmap.Height; row++) {
-        int col;
-        for (col = 0; col < bitmap.Width;) {
-            /* A little ugliness makes a big speed difference here. */
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<7);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<6);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<5);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<4);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<3);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<2);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<1);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<0);
-                
-            bitmap_cursor++;
-        }
-        pbm_writepbmrow(file, pbmrow, bitmap.Width, forceplain); 
+    for (row = 0; row < bitmap.Height; ++row) {
+        pbm_writepbmrow_packed(ofP, bitmap.bitmap[row],
+                               bitmap.Width, forceplain); 
     }
-    pbm_freerow(pbmrow);
-    pm_close(file);
 }
 
 
+
 static void
 framePerimeter(unsigned int const Width, 
                unsigned int const Height) {
 
-    unsigned int x,y;
+    unsigned int x, y;
 
     /* Top edge */
     for (x = 0; x < Width; ++x)
@@ -221,57 +217,66 @@ framePerimeter(unsigned int const Width,
 
 
 int 
-main(int argc,char** argv) {
+main(int argc, const char** argv) {
 
-    int TP=1;
-    int x,y;
+    int TP;
+    unsigned int x, y;
     char buf[128];
-    int Width;      /* width and height in 600ths of an inch */
-    int Height;
+    /* width and height in 600ths of an inch */
+    unsigned int Width;
+    unsigned int Height;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     if (argc > 1 && strcmp(argv[1], "-a4") == 0) {
-        Width = A4WIDTH;
+        Width  = A4WIDTH;
         Height = A4HEIGHT;
-        argc--;
-        argv++;
+        --argc;
+        ++argv;
     } else {
-        Width = USWIDTH;
+        Width  = USWIDTH;
         Height = USHEIGHT;
     }
 
-    bitmap.Width = Width;
+    if (argc > 1)
+        TP = atoi(argv[1]);
+    else
+        TP = 1;
+
+    bitmap.Width  = Width;
     bitmap.Height = Height;
-    bitmap.Pwidth = (Width + 7) / 8;
-    bitmap.bitmap = malloc(bitmap.Pwidth * bitmap.Height);
+    bitmap.bitmap = pbm_allocarray_packed(Width, bitmap.Height);
 
-    for (x = 0; x < bitmap.Pwidth * bitmap.Height; ++x)
-        bitmap.bitmap[x] = 0x00;
-    
-    if (argc>1)
-        TP = atoi(argv[1]);
+    for (y = 0; y < bitmap.Height; ++y) {
+        unsigned int x;
+        for (x = 0; x < pbm_packed_bytes(bitmap.Width); ++x) 
+            bitmap.bitmap[y][x] = 0x00; 
+    }
 
     switch (TP) {
     case 1:
         framePerimeter(Width, Height);
-        for (x = 0; x < Width; x += 100)
+        for (x = 0; x < Width; x += 100) {
+            unsigned int y;
             for(y = 0; y < Height; y += 100)
                 setplus(x, y, 4);
+        }
         for(x = 0; x < Width; x += 100) {
             sprintf(buf,"%d", x);
             setstring(x + 3, (Height/200) * 100 + 3, buf);
         }
-        for (y=0; y < Height; y += 100) {
+        for (y = 0; y < Height; y += 100) {
             sprintf(buf, "%d", y);
             setstring((Width/200) * 100 + 3, y + 3, buf);
         }
         for (x = 0; x < Width; x += 10)
             for (y = 0; y < Height; y += 100)
                 setplus(x, y, ((x%100) == 50) ? 2 : 1);
-        for (x=0; x < Width; x += 100)
+        for (x = 0; x < Width; x += 100) {
+            unsigned int y;
             for (y = 0; y < Height; y += 10)
                 setplus(x, y, ((y%100) == 50) ? 2 : 1);
+        }
         setCG(Width/2, Height/2);
         break;
     case 2:
@@ -290,5 +295,9 @@ main(int argc,char** argv) {
 
     outputPbm(stdout, bitmap);
 
+    pbm_freearray(bitmap.bitmap, Height);
+
+    pm_close(stdout);
+
     return 0;
 }
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index 693c3f59..9f4366d4 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -79,7 +79,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0)
@@ -236,7 +236,8 @@ fixControlChars(const char *  const input,
     MALLOCARRAY(output, outputSize);
 
     if (output == NULL)
-        pm_error("Couldn't allocate %u bytes for a line of text.", outputSize);
+        pm_error("Couldn't allocate %u bytes for a line of text.",
+                 (unsigned)outputSize);
 
     for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) {
         if (outCursor + 1 + tabSize > outputSize) {
@@ -244,7 +245,7 @@ fixControlChars(const char *  const input,
             REALLOCARRAY(output, outputSize);
             if (output == NULL)
                 pm_error("Couldn't allocate %u bytes for a line of text.",
-                         outputSize);
+                         (unsigned)outputSize);
         }
         if (input[inCursor] == '\n' && input[inCursor+1] == '\0') {
             /* This is a terminating newline.  We don't do those. */
@@ -709,7 +710,7 @@ getText(const char          cmdline_text[],
         while (fgets(buf, sizeof(buf), stdin) != NULL) {
             if (strlen(buf) + 1 >= sizeof(buf))
                 pm_error("A line of input text is longer than %u characters."
-                         "Cannot process.", sizeof(buf)-1);
+                         "Cannot process.", (unsigned)sizeof(buf)-1);
             if (lineCount >= maxlines) {
                 maxlines *= 2;
                 REALLOCARRAY(text_array, maxlines);
diff --git a/generator/pbmtextps.c b/generator/pbmtextps.c
index f879fa88..e2775bb9 100644
--- a/generator/pbmtextps.c
+++ b/generator/pbmtextps.c
@@ -140,7 +140,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     buildTextFromArgs(argc, argv, &cmdlineP->text);
 }
@@ -174,11 +174,11 @@ construct_postscript(struct cmdlineInfo const cmdline) {
             "showpage\n";
 
     if (cmdline.stroke < 0)
-        asprintfN(&retval, template, cmdline.font, cmdline.fontsize, 
-                  cmdline.text);
+        pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, 
+                    cmdline.text);
     else
-        asprintfN(&retval, template, cmdline.font, cmdline.fontsize, 
-                  cmdline.stroke, cmdline.text);
+        pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, 
+                    cmdline.stroke, cmdline.text);
 
     return retval;
 }
@@ -273,10 +273,11 @@ gsCommand(const char *       const psFname,
          pm_error("Absurdly fine resolution (%u) and/or huge font size (%u). "
                   "Output height too large.", cmdline.res, cmdline.fontsize);
          
-    asprintfN(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm "
-              "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s </dev/null >/dev/null", 
-              gsExecutableName(), (int) x, (int) y, cmdline.res, 
-              outputFilename, psFname);
+    pm_asprintf(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm "
+                "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s "
+                "</dev/null >/dev/null", 
+                gsExecutableName(), (int) x, (int) y, cmdline.res, 
+                outputFilename, psFname);
 
     return retval;
 }
@@ -290,9 +291,9 @@ cropCommand(const char * const inputFileName) {
     const char * plainOpt = pm_plain_output ? "-plain" : "" ;
     
     if (cropExecutableName()) {
-        asprintfN(&retval, "%s -top -right %s %s", 
-                  cropExecutableName(), plainOpt, inputFileName);
-        if (retval == strsol)
+        pm_asprintf(&retval, "%s -top -right %s %s", 
+                    cropExecutableName(), plainOpt, inputFileName);
+        if (retval == pm_strsol)
             pm_error("Unable to allocate memory");
     } else
         retval = NULL;
@@ -324,7 +325,7 @@ writeProgram(const char *       const psFname,
 
     fclose(psfile);
 
-    strfree(ps);
+    pm_strfree(ps);
 }
 
 
@@ -348,7 +349,7 @@ executeProgram(const char *       const psFname,
     if (rc != 0)
         pm_error("Failed to run Ghostscript process.  rc=%d", rc);
 
-    strfree(com);
+    pm_strfree(com);
 }
 
 
@@ -397,7 +398,7 @@ cropToStdout(const char * const inputFileName,
             }
             fclose(pnmcrop);
         }
-        strfree(com);
+        pm_strfree(com);
     }
 }
 
@@ -411,25 +412,25 @@ createOutputFile(struct cmdlineInfo const cmdline) {
     const char * psFname;
     const char * uncroppedPbmFname;
 
-    asprintfN(&psFname, template, getpid(), "ps");
+    pm_asprintf(&psFname, template, getpid(), "ps");
     if (psFname == NULL)
         pm_error("Unable to allocate memory");
  
     writeProgram(psFname, cmdline);
 
-    asprintfN(&uncroppedPbmFname, template, getpid(), "pbm");
+    pm_asprintf(&uncroppedPbmFname, template, getpid(), "pbm");
     if (uncroppedPbmFname == NULL)
         pm_error("Unable to allocate memory");
  
     executeProgram(psFname, uncroppedPbmFname, cmdline);
 
     unlink(psFname);
-    strfree(psFname);
+    pm_strfree(psFname);
 
     cropToStdout(uncroppedPbmFname, cmdline.verbose);
 
     unlink(uncroppedPbmFname);
-    strfree(uncroppedPbmFname);
+    pm_strfree(uncroppedPbmFname);
 }
 
 
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 ec592381..00000000
--- a/generator/pgmcrater.c
+++ /dev/null
@@ -1,382 +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"
-
-static void gencraters ARGS((void));
-static void initseed ARGS((void));
-
-/* Definitions for obtaining random numbers. */
-
-#define Cast(low, high) ((low)+((high)-(low)) * ((rand() & 0x7FFF) / arand))
-
-/*  Data types	*/
-
-typedef int Boolean;
-#define FALSE 0
-#define TRUE 1
-
-#define V   (void)
-
-/*  Display parameters	*/
-
-#define SCRX	screenxsize	      /* Screen width */
-#define SCRY	screenysize	      /* Screen height */
-#define SCRGAMMA 1.0		      /* Display gamma */
-
-/*  Local variables  */
-
-#define ImageGamma  0.5 	      /* Inherent gamma of mapped image */
-
-static int screenxsize = 256;	      /* Screen X size */
-static int screenysize = 256;	      /* Screen Y size */
-static double dgamma = SCRGAMMA;      /* Display gamma */
-static double arand = 32767.0;	      /* Random number parameters */
-static long ncraters = 50000L;	      /* Number of craters to generate */
-static double CdepthPower = 1.5;      /* Crater depth power factor */
-static double DepthBias = 0.707107;   /* Depth bias */
-
-static int modulo(int t, int n)
-{
-    int m;
-    assert(n>0);
-    m = t % n;
-    while (m<0) {
-	m+=n;
-    }
-    return m;
-}
-
-/*  INITSEED  --  Generate initial random seed, if needed.  */
-
-static void initseed()
-{
-    unsigned int i;
-
-    srand(pm_randseed());
-    for (i = 0; i < 7; ++i) 
-        V rand();
-}
-
-/*  GENCRATERS	--  Generate cratered terrain.	*/
-
-static void gencraters()
-{
-    int i, j, x, y;
-    long l;
-    unsigned short *aux;
-    int slopemin = -52, slopemax = 52;
-#define RGBQuant    255
-    unsigned char *slopemap;   /* Slope to pixel map */
-    gray *pixels;	       /* Pixel vector */
-
-#define Auxadr(x, y)  &aux[modulo(y, SCRY)*SCRX+modulo(x, SCRX)]
-
-    /* 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 long 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 0
-	fprintf(stderr, "crater=%lu ", (unsigned long)l);
-	fprintf(stderr, "cx=%d ", cx);
-	fprintf(stderr, "cy=%d ", cy);
-	fprintf(stderr, "size=%g\n", g);
-#endif
-
-	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( "%ld craters generated of %ld (%ld%% 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));
-    }
-
-    /* Generate the screen image. */
-
-    pgm_writepgminit(stdout, SCRX, SCRY, RGBQuant, FALSE);
-    pixels = pgm_allocrow(SCRX);
-
-    for (y = 0; y < SCRY; y++) {
-	gray *pix = pixels;
-
-	for (x = 0; x < SCRX; x++) {
-	    int j = *Auxadr(x+1, y) - *Auxadr(x, y);
-	    j = MIN(MAX(slopemin, j), slopemax);
-	    *pix++ = slopemap[j - slopemin];
-	}
-	pgm_writepgmrow(stdout, pixels, SCRX, RGBQuant, FALSE);
-    }
-    pm_close(stdout);
-    pgm_freerow(pixels);
-
-#undef Auxadr
-#undef Scradr
-    free((char *) slopemap);
-    free((char *) aux);
-}
-
-/*  MAIN  --  Main program.  */
-
-int main(argc, argv)
-  int argc;
-  char *argv[];
-{
-    int i;
-    Boolean gammaspec = FALSE, numspec = FALSE,
-	    widspec = FALSE, hgtspec = FALSE;
-    const char * const usage = "[-number <n>] [-width|-xsize <w>]\n\
-                  [-height|-ysize <h>] [-gamma <f>]";
-
-    DepthBias = sqrt(0.5);	      /* Get exact value for depth bias */
-
-
-    pgm_init(&argc, argv);
-
-    i = 1;
-    while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) {
-        if (pm_keymatch(argv[i], "-gamma", 2)) {
-	    if (gammaspec) {
-                pm_error("already specified gamma correction");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &dgamma)  != 1))
-		pm_usage(usage);
-	    if (dgamma <= 0.0) {
-                pm_error("gamma correction must be greater than 0");
-	    }
-	    gammaspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-number", 2)) {
-	    if (numspec) {
-                pm_error("already specified number of craters");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%ld", &ncraters) != 1))
-		pm_usage(usage);
-	    if (ncraters <= 0) {
-                pm_error("number of craters must be greater than 0!");
-	    }
-	    numspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-xsize", 2) ||
-                   pm_keymatch(argv[i], "-width", 2)) {
-	    if (widspec) {
-                pm_error("already specified a width/xsize");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1))
-		pm_usage(usage);
-	    if (screenxsize <= 0) {
-                pm_error("screen width must be greater than 0");
-	    }
-	    widspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-ysize", 2) ||
-                   pm_keymatch(argv[i], "-height", 2)) {
-	    if (hgtspec) {
-                pm_error("already specified a height/ysize");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1))
-		pm_usage(usage);
-	    if (screenxsize <= 0) {
-                pm_error("screen height must be greater than 0");
-	    }
-	    hgtspec = TRUE;
-	} else {
-	    pm_usage(usage);
-	}
-	i++;
-    }
-
-    initseed();
-    gencraters();
-
-    exit(0);
-}
diff --git a/generator/pgmkernel.c b/generator/pgmkernel.c
index b741d596..ec634c16 100644
--- a/generator/pgmkernel.c
+++ b/generator/pgmkernel.c
@@ -1,8 +1,8 @@
-/* pgmkernel.c - generate a portable graymap convolution kernel
+/* pgmkernel.c - generate a PGM convolution kernel
 **
-** Creates a Portable Graymap file containing a convolution filter
-** with max value = 255 and minimum value > 127 that can be used as a 
-** smoothing kernel for pnmconvol.
+** Creates a PGM image containing a convolution filter with max value = 255
+** and minimum value > 127 that can be used as a smoothing kernel for
+** pnmconvol.
 **
 ** Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical
 ** Observatory.
@@ -16,76 +16,228 @@
 */
 
 #include <math.h>
-#include "pgm.h"
+#include "pm_c_util.h"
+#include "shhopt.h"
 #include "mallocvar.h"
+#include "pgm.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int cols;
+    unsigned int rows;
+    float weight;
+    gray maxval;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+  Convert program invocation arguments (argc,argv) into a format the 
+  program can use easily, struct cmdlineInfo.  Validate arguments along
+  the way and exit program with message if invalid.
+
+  Note that some string information we return as *cmdlineP is in the storage 
+  argv[] points to.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int weightSpec, maxvalSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "weight",  OPT_FLOAT, &cmdlineP->weight, 
+            &weightSpec,     0);
+    OPTENT3(0,   "maxval",  OPT_UINT, &cmdlineP->maxval, 
+            &maxvalSpec,     0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!weightSpec)
+        cmdlineP->weight = 6.0;
+
+    if (cmdlineP->weight < 0.0)
+        pm_error("-weight cannot be negative.  You specified %f",
+                 cmdlineP->weight);
+
+    if (!maxvalSpec)
+        cmdlineP->maxval = PGM_MAXMAXVAL;
+
+    if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
+        pm_error("-maxval is too large: %u.  Maximum is %u",
+                 cmdlineP->maxval, PGM_OVERALLMAXVAL);
+
+    if (cmdlineP->maxval == 0)
+        pm_error("-maxval cannot be zero");
+
+    if (argc-1 < 1)
+        pm_error("Need at least one argument: size of (square) kernel");
+    else if (argc-1 == 1) {
+        if (atoi(argv[1]) <= 0)
+            pm_error("Dimension must be a positive number.  "
+                     "You specified '%s'", argv[1]);
+        cmdlineP->cols = atoi(argv[1]);
+        cmdlineP->rows = atoi(argv[1]);
+    } else if (argc-1 == 2) {
+        if (atoi(argv[1]) <= 0)
+            pm_error("Width must be a positive number.  "
+                     "You specified '%s'", argv[1]);
+        if (atoi(argv[2]) <= 0)
+            pm_error("Height must be a positive number.  "
+                     "You specified '%s'", argv[2]);
+        cmdlineP->cols = atoi(argv[1]);
+        cmdlineP->rows = atoi(argv[2]);
+    } else
+        pm_error("At most two arguments allowed.  "
+                 "You specified %u", argc-1);
+}
+
+
+
+static double
+t(double const dx2,
+  double const dy2,
+  double const weight) {
+/*----------------------------------------------------------------------------
+  The t value for a pixel that is (dx, dy) pixels away from the center of
+  the kernel, where 'dx2' is SQR(dx) and 'dy2' is SQR(dy), if the distance is
+  weighted by 'weight'.
+-----------------------------------------------------------------------------*/
+
+    return 1.0 / (1.0 + weight * sqrt(dx2 + dy2));
+}
+
+
+
+static double
+tMaxAllKernel(unsigned int const cols,
+              unsigned int const rows,
+              double       const weight) {
+/*----------------------------------------------------------------------------
+   The maximum t value over all pixels in the kernel, if the kernel is
+   'cols' by 'rows' pixels and distance is weighted by 'weight'.
+-----------------------------------------------------------------------------*/
+
+    /* It depends upon whether there is an even or odd number of rows
+       and columns.  If both dimensions are odd, there is a pixel right
+       at the center, and it has the greatest t value.  If both dimensions
+       are even, the center of the image is in the center of a 4-pixel
+       square and each of those 4 pixels has the greatest t value.  If
+       one dimension is even and the other odd, the center of the kernel
+       is midway between two pixels, horizontally or vertically, and one
+       of those two pixels has the greatest t value.
+    */
+
+    double dxMax, dyMax;
+
+    switch (cols % 2 + rows % 2) {
+    case 0:
+        dxMax = 0.5;
+        dyMax = 0.5;
+        break;
+    case 1:
+        dxMax = 0.5;
+        dyMax = 0.0;
+        break;
+    case 2:
+        dxMax = 0.0;
+        dyMax = 0.0;
+    }
+
+    return t(SQR(dxMax), SQR(dyMax), weight);
+}
+
+
+
+static void
+writeKernel(FILE *       const ofP,
+            unsigned int const cols,
+            unsigned int const rows,
+            gray         const maxval,
+            gray **      const halfKernel,
+            unsigned int const halfRows) {
+
+    unsigned int row;
+
+    pgm_writepgminit(stdout, cols, rows, maxval, 0);
+
+    for (row = 0; row < halfRows; ++row)
+        pgm_writepgmrow(stdout, halfKernel[row], cols, maxval, 0);
+
+    /* Now write out the same rows in reverse order. */
+
+    for (; row < rows; ++row)
+        pgm_writepgmrow(stdout, halfKernel[rows-1-row], cols, maxval, 0);
+}
+
+
 
 int
-main ( argc, argv )
-    int argc;
-    char *argv[];
-{
-    register    int i, j;
-    int     argn = 1, ixsize, iysize, maxval = 255;
-    double  fxsize = 0.0, fysize = 0.0, w = 6.0, kxcenter, kycenter, 
-        tmax = 0, *fkernel;
-    const char  *usage = "[-weight f] width [height]";
-
-    pgm_init( &argc, argv );
-
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-weight", 2 )) {
-            if (++argn >= argc)
-                pm_usage( usage );
-            else if (sscanf(argv[argn], "%lf", &w) != 1)
-                pm_usage( usage );
+main(int argc, const char * argv[]) {
+
+    struct CmdlineInfo cmdline;
+    unsigned int arows;
+    unsigned int arow;
+    double xcenter, ycenter;
+        /* row, column "number" of center of kernel */
+    double tMax;
+        /* The maximum t value over all pixels */
+    gray ** halfKernel;
+        /* The upper half of the kernel we generate.  The lower half is
+           just the mirror image of this.
+        */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    xcenter = ((double) cmdline.cols - 1) / 2.0;
+    ycenter = ((double) cmdline.rows - 1) / 2.0;
+
+    tMax = tMaxAllKernel(cmdline.cols, cmdline.rows, cmdline.weight);
+
+    /* Output matrix is symmetric vertically and horizontally. */
+
+    arows = (cmdline.rows + 1) / 2;
+        /* Half the number of rows.  Add 1 if odd. */
+    halfKernel = pgm_allocarray(cmdline.cols, arows);
+
+    for (arow = 0; arow < arows; ++arow) {
+        double const dy2 = SQR(arow - ycenter);
+
+        unsigned int col;
+        for (col = 0; col < (cmdline.cols +1) / 2; ++col) {
+            double const dx2 = SQR(col - xcenter);
+
+            double const normalized = t(dx2, dy2, cmdline.weight) / 2 / tMax;
+
+            gray const grayval = ROUNDU(cmdline.maxval * (0.5 + normalized));
+
+            halfKernel[arow][col                   ] = grayval;
+            halfKernel[arow][cmdline.cols - col - 1] = grayval;
         }
-        else
-            pm_usage( usage );
-        argn++;
     }
 
-    if (argn == argc)
-        pm_usage( usage );
-    
-    if (sscanf(argv[argn], "%lf", &fxsize) != 1) 
-        pm_error( "error reading input kernel x size, (%s)\n", argv[argn]);
+    writeKernel(stdout, cmdline.cols, cmdline.rows, cmdline.maxval,
+                halfKernel, arows);
 
-    ++argn;
-    if (argn == argc - 1) {
-        if (sscanf(argv[argn], "%lf", &fysize) != 1)
-            pm_error( "error reading input kernel y size, (%s)\n", argv[argn]);
-    }
-    else if (argn == argc)
-        fysize = fxsize;
-    else
-        pm_usage( usage );
-
-    if (fxsize <= 1 || fysize <= 1)
-        pm_usage( usage );
-
-    kxcenter = (fxsize - 1) / 2.0;
-    kycenter = (fysize - 1) / 2.0;
-    ixsize = fxsize + 0.999;
-    iysize = fysize + 0.999;
-    MALLOCARRAY(fkernel, ixsize * iysize);
-    for (i = 0; i < iysize; i++) 
-        for (j = 0; j < ixsize; j++) {
-            fkernel[i*ixsize+j] = 1.0 / (1.0 + w * sqrt((double)
-                                                        (i-kycenter)*(i-kycenter)+
-                                                        (j-kxcenter)*(j-kxcenter)));
-            if (tmax < fkernel[i*ixsize+j])
-                tmax = fkernel[i*ixsize+j];
-        }
+    pgm_freearray(halfKernel, arows);
 
-    /* output PGM header + data (ASCII format only) */
-    printf("P2\n%d %d\n%d\n", ixsize, iysize, maxval);
-    
-    for (i = 0; i < iysize; i++, printf("\n"))
-        for (j = 0; j < ixsize; j++)
-            printf(" %3d", (int)(maxval * (fkernel[i*ixsize+j] / 
-                                           (2*tmax) + 0.5)));
-    
-    exit(0);
+    return 0;
 }
-
diff --git a/generator/pgmmake.c b/generator/pgmmake.c
index bc7f025c..f8f8b09c 100644
--- a/generator/pgmmake.c
+++ b/generator/pgmmake.c
@@ -43,19 +43,21 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = false;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free (option_def);
+
     if (!maxvalSpec)
         cmdlineP->maxval = PGM_MAXMAXVAL;
     else {
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 3)
         pm_error("Need 3 arguments: gray level, width, height.");
@@ -82,7 +84,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     gray * grayrow;
-    unsigned int row;
+    unsigned int col, row;
 
     pgm_init(&argc, argv);
 
@@ -91,12 +93,12 @@ main(int argc, char *argv[]) {
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     grayrow = pgm_allocrow(cmdline.cols);
 
-    for (row = 0; row < cmdline.rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cmdline.cols; ++col)
-            grayrow[col] = cmdline.grayLevel;
+    /* All rows are identical.  Fill once. */
+    for (col = 0; col < cmdline.cols; ++col)
+        grayrow[col] = cmdline.grayLevel;
+
+    for (row = 0; row < cmdline.rows; ++row)
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
-	}
 
     pgm_freerow(grayrow);
     pm_close(stdout);
diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c
index 215cbfeb..442edc59 100644
--- a/generator/pgmnoise.c
+++ b/generator/pgmnoise.c
@@ -7,6 +7,8 @@
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pgm.h"
+#include <assert.h>
+
 
 
 struct cmdlineInfo {
@@ -15,13 +17,13 @@ struct cmdlineInfo {
     */
     unsigned int width;
     unsigned int height;
+    unsigned int maxval;
     unsigned int randomseed;
     unsigned int randomseedSpec;
 };
 
 
 
-
 static void
 parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -34,19 +36,32 @@ parseCommandLine(int argc, const char ** const argv,
          */
     optStruct3 opt;
     unsigned int option_def_index;
+    unsigned int maxvalSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "randomseed",   OPT_INT,    &cmdlineP->randomseed,
+    OPTENT3(0,   "randomseed",   OPT_UINT,    &cmdlineP->randomseed,
             &cmdlineP->randomseedSpec,      0);
+    OPTENT3(0,   "maxval",       OPT_UINT,    &cmdlineP->maxval,
+            &maxvalSpec,                    0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
+
+    if (maxvalSpec) {
+        if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
+            pm_error("Maxval too large: %u.  Maximu is %u", 
+                     cmdlineP->maxval, PGM_OVERALLMAXVAL);
+        else if (cmdlineP->maxval == 0)
+            pm_error("Maxval must not be zero");
+    } else
+        cmdlineP->maxval = PGM_MAXMAXVAL;
 
     if (argc-1 != 2)
         pm_error("Wrong number of arguments: %u.  "
@@ -70,25 +85,91 @@ parseCommandLine(int argc, const char ** const argv,
 
 
 
+static unsigned int
+randPool(unsigned int const digits) {
+/*----------------------------------------------------------------------------
+  Draw 'digits' bits from pool of random bits.  If the number of random bits
+  in pool is insufficient, call rand() and add 31 bits to it.
+  
+  'digits' must be at most 16.
+
+  We assume that each call to rand() generates 31 bits, or RAND_MAX ==
+  2147483647.
+  
+  The underlying logic is flexible and endian-free.  The above conditions
+  can be relaxed.
+-----------------------------------------------------------------------------*/
+    static unsigned long int hold=0;  /* entropy pool */
+    static unsigned int len=0;        /* number of valid bits in pool */
+
+    unsigned int const mask = (1 << digits) - 1;
+
+    unsigned int retval;
+
+    assert(RAND_MAX == 2147483647 && digits <= 16);
+
+    retval = hold;  /* initial value */
+
+    if (len > digits) { /* Enough bits in hold to satisfy request */
+        hold >>= digits;
+        len   -= digits;
+    } else {              /* Load another 31 bits into hold */
+        hold    = rand(); 
+        retval |= (hold << len);
+        hold >>=  (digits - len);
+        len = 31 - digits + len;
+    }
+    return (retval & mask);
+}
+
+
 
 static void
-pgmnoise(FILE * const ofP,
+pgmnoise(FILE *       const ofP,
          unsigned int const cols,
          unsigned int const rows,
          gray         const maxval) {
 
+    bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1)));
+    unsigned int const bitLen = pm_maxvaltobits(maxval);
+
     unsigned int row;
     gray * destrow;
 
+    /* If maxval is 2^n-1, we draw exactly n bits from the pool.
+       Otherwise call rand() and determine gray value by modulo.
+
+       In the latter case, there is a miniscule skew toward 0 (=black)
+       because smaller numbers are produced more frequently by modulo.
+       Thus we employ the pool method only when it is certain that no
+       skew will ensue.
+
+       To illustrate the point, consider converting the outcome of one
+       roll of a fair, six-sided die to 5 values (0 to 4) by N % 5.  The
+       probability for values 1, 2, 3, 4 are 1/6, but 0 alone is 2/6.
+       Average is 10/6 or 1.6667, compared to 2.0 from an ideal
+       generator which produces exactly 5 values.  With two dice
+       average improves to 70/36 or 1.9444.
+
+       The more (distinct) dice we roll, or the more binary digits we
+       draw, the smaller the skew.
+    */
+
     destrow = pgm_allocrow(cols);
 
     pgm_writepgminit(ofP, cols, rows, maxval, 0);
 
     for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cols; ++col)
-            destrow[col] = rand() % (maxval + 1);
-
+        if (usingPool) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                destrow[col] = randPool(bitLen);
+        } 
+        else { 
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                destrow[col] = rand() % (maxval + 1); 
+        }
         pgm_writepgmrow(ofP, destrow, cols, maxval, 0);
     }
 
@@ -97,8 +178,9 @@ pgmnoise(FILE * const ofP,
 
 
 
-int main(int          argc,
-         const char * argv[]) {
+int
+main(int          argc,
+     const char * argv[]) {
     
     struct cmdlineInfo cmdline;
 
@@ -108,8 +190,7 @@ int main(int          argc,
 
     srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
-    pgmnoise(stdout, cmdline.width, cmdline.height, PGM_MAXMAXVAL);
+    pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval);
 
     return 0;
 }
-
diff --git a/generator/pgmramp.c b/generator/pgmramp.c
index c0fb42b1..225542fe 100644
--- a/generator/pgmramp.c
+++ b/generator/pgmramp.c
@@ -16,7 +16,7 @@
 #include "pgm.h"
 #include "shhopt.h"
 
-enum ramptype {RT_LR, RT_TB, RT_RECT, RT_ELLIP};
+enum ramptype {RT_LR, RT_TB, RT_DIAG, RT_RECT, RT_ELLIP};
 
 
 struct cmdlineInfo {
@@ -39,7 +39,7 @@ parseCommandLine(int argc, char ** argv,
   program can use easily, struct cmdlineInfo.  Validate arguments along
   the way and exit program with message if invalid.
 
-  Note that some string information we return as *cmdlineP is in the storage 
+  Note that some string information we return as *cmdlineP is in the storage
   argv[] points to.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc(100*sizeof(optEntry));
@@ -47,13 +47,14 @@ parseCommandLine(int argc, char ** argv,
          */
     optStruct3 opt;
 
-    unsigned int lrSpec, tbSpec, rectangleSpec, ellipseSpec;
+    unsigned int lrSpec, tbSpec, diagonalSpec, rectangleSpec, ellipseSpec;
     unsigned int maxvalSpec;
     unsigned int option_def_index;
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "lr",        OPT_FLAG, NULL,              &lrSpec,        0);
     OPTENT3(0,   "tb",        OPT_FLAG, NULL,              &tbSpec,        0);
+    OPTENT3(0,   "diagonal",  OPT_FLAG, NULL,              &diagonalSpec,  0);
     OPTENT3(0,   "rectangle", OPT_FLAG, NULL,              &rectangleSpec, 0);
     OPTENT3(0,   "ellipse",   OPT_FLAG, NULL,              &ellipseSpec,   0);
     OPTENT3(0,   "maxval",    OPT_UINT, &cmdlineP->maxval, &maxvalSpec,    0);
@@ -62,18 +63,23 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (lrSpec + tbSpec + rectangleSpec + ellipseSpec == 0)
-        pm_error("You must specify one of -lr, -tb, -rectangle, or -ellipse");
-    if (lrSpec + tbSpec + rectangleSpec + ellipseSpec > 1)
+    free (option_def);
+
+    if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec == 0)
+        pm_error("You must specify one of "
+                 "-lr, -tb, -diagonal, -rectangle, or -ellipse");
+    if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec > 1)
         pm_error("You may specify at most one of "
-                 "-lr, -tb, -rectangle, or -ellipse");
+                 "-lr, -tb, -diagonal, -rectangle, or -ellipse");
     if (lrSpec)
         cmdlineP->ramptype = RT_LR;
     else if (tbSpec)
         cmdlineP->ramptype = RT_TB;
+    else if (diagonalSpec)
+        cmdlineP->ramptype = RT_DIAG;
     else if (rectangleSpec)
         cmdlineP->ramptype = RT_RECT;
     else if (ellipseSpec)
@@ -87,10 +93,10 @@ parseCommandLine(int argc, char ** argv,
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 2)
         pm_error("Need two arguments: width and height.");
@@ -105,7 +111,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-int 
+int
 main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
@@ -119,31 +125,36 @@ main(int argc, char *argv[]) {
 
     colso2 = MAX(1, cmdline.cols / 2);
     rowso2 = MAX(1, cmdline.rows / 2);
-    
+
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     grayrow = pgm_allocrow(cmdline.cols);
-    
+
     for (row = 0; row < cmdline.rows; ++row) {
         unsigned int col;
         for (col = 0; col < cmdline.cols; ++col) {
             switch (cmdline.ramptype) {
             case RT_LR:
-                grayrow[col] = 
-                    col * cmdline.maxval / MAX(cmdline.cols-1, 1);
+                /* Fill row buffer once.  All rows are identical. */
+                if (row == 0)
+                    grayrow[col] =
+                        (float) col * cmdline.maxval / MAX(cmdline.cols-1, 1);
                 break;
             case RT_TB:
-                grayrow[col] = 
-                    row * cmdline.maxval / MAX(cmdline.rows-1, 1);
+                grayrow[col] =
+                    (float) row * cmdline.maxval / MAX(cmdline.rows-1, 1);
+                break;
+            case RT_DIAG:
+                grayrow[col] =
+                    ((float) col + row) * cmdline.maxval /
+                        MAX((float) cmdline.cols + cmdline.rows-2, 1);
                 break;
-
             case RT_RECT: {
                 float const r = fabs((int)(rowso2 - row)) / rowso2;
                 float const c = fabs((int)(colso2 - col)) / colso2;
-                grayrow[col] = 
+                grayrow[col] =
                     cmdline.maxval - (r + c) / 2.0 * cmdline.maxval;
-            }
-            break;
-            
+            } break;
+
             case RT_ELLIP: {
                 float const r = fabs((int)(rowso2 - row)) / rowso2;
                 float const c = fabs((int)(colso2 - col)) / colso2;
@@ -153,12 +164,11 @@ main(int argc, char *argv[]) {
                 if ( v < 0.0 ) v = 0.0;
                 else if ( v > 1.0 ) v = 1.0;
                 grayrow[col] = cmdline.maxval - v * cmdline.maxval;
+            } break;
             }
-            break;
-            }
-	    }
+        }
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
-	}
+    }
 
     pgm_freerow(grayrow);
     pm_close(stdout);
diff --git a/generator/ppmcie.c b/generator/ppmcie.c
index fda0ab7c..717ed13b 100644
--- a/generator/ppmcie.c
+++ b/generator/ppmcie.c
@@ -26,6 +26,7 @@
   Introduced option to plot 1976 u' v' chromaticities.
 */
 
+#include <assert.h>
 #include <math.h>
 
 #include "pm_c_util.h"
@@ -34,10 +35,8 @@
 #include "nstring.h"
 
 #define CLAMP(v, l, h)  ((v) < (l) ? (l) : (v) > (h) ? (h) : (v))
-#define TRUE    1
-#define FALSE   0
 
-#define Maxval  255                   /* Maxval to use in generated pixmaps */
+pixval const cieMaxval = 255;  /* Maxval to use in generated pixmaps */
 
 /* A  color  system is defined by the CIE x and y  coordinates of its
    three primary illuminants and the x and y coordinates of the  white
@@ -462,8 +461,18 @@ gamma_correct_rgb(const struct colorSystem * const cs,
 
 
 
-#define Sz(x) (((x) * MIN(pixcols, pixrows)) / 512)
+/* Sz(X) is the displacement in pixels of a displacement of X normalized
+   distance units.  (A normalized distance unit is 1/512 of the smaller
+   dimension of the canvas)
+*/
+#define Sz(x) (((x) * (int)MIN(pixcols, pixrows)) / 512)
+
+/* B(X, Y) is a pair of function arguments (for a libppmd function) that
+   give a pixel position on the canvas.  That position is (X, Y), biased
+   horizontally 'xBias' pixels.
+*/
 #define B(x, y) ((x) + xBias), (y)
+
 #define Bixels(y, x) pixels[y][x + xBias]
 
 
@@ -511,12 +520,12 @@ makeAllBlack(pixel **     const pixels,
 
 static void
 drawTongueOutline(pixel ** const pixels,
-                  int    const pixcols,
-                  int    const pixrows,
-                  pixval const maxval,
-                  bool   const upvp,
-                  int    const xBias,
-                  int    const yBias) {
+                  int      const pixcols,
+                  int      const pixrows,
+                  pixval   const maxval,
+                  bool     const upvp,
+                  int      const xBias,
+                  int      const yBias) {
 
     int const pxcols = pixcols - xBias;
     int const pxrows = pixrows - yBias;
@@ -535,7 +544,7 @@ drawTongueOutline(pixel ** const pixels,
                                        &icx, &icy);
         
         if (wavelength > 380)
-            ppmd_line(pixels, pixcols, pixrows, Maxval,
+            ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(lx, ly), B(icx, icy),
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
         else {
@@ -574,12 +583,12 @@ findTongue(pixel ** const pixels,
          ++i);
 
     if (i >= pxcols)
-        *presentP = FALSE;
+        *presentP = false;
     else {
         int j;
         int const leftEdge = i;
 
-        *presentP = TRUE;
+        *presentP = true;
         
         for (j = pxcols - 1;
              j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0;
@@ -641,16 +650,16 @@ fillInTongue(pixel **                   const pixels,
 
                 xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb);
 
-                mx = Maxval;
+                mx = maxval;
         
                 /* Check whether the requested color  is  within  the
                    gamut  achievable with the given color system.  If
                    not, draw it in a reduced  intensity,  interpolated
                    by desaturation to the closest within-gamut color. */
         
-                if (constrain_rgb(&jr, &jg, &jb)) {
-                    mx = highlightGamut ? Maxval : ((Maxval + 1) * 3) / 4;
-                }
+                if (constrain_rgb(&jr, &jg, &jb))
+                    mx = highlightGamut ? maxval : ((maxval + 1) * 3) / 4;
+
                 /* Scale to max(rgb) = 1. */
                 jmax = MAX(jr, MAX(jg, jb));
                 if (jmax > 0) {
@@ -672,67 +681,175 @@ fillInTongue(pixel **                   const pixels,
 
 
 static void
-drawAxes(pixel ** const pixels,
-         int    const pixcols,
-         int    const pixrows,
-         pixval const maxval,
-         bool   const upvp,
-         int    const xBias,
-         int    const yBias) {
+drawYAxis(pixel **     const pixels,
+          unsigned int const pixcols,
+          unsigned int const pixrows,
+          pixval       const maxval,
+          unsigned int const xBias,
+          unsigned int const yBias,
+          pixel        const axisColor) {
+              
+    unsigned int const pxrows = pixrows - yBias;
 
-    int const pxcols = pixcols - xBias;
-    int const pxrows = pixrows - yBias;
-
-    pixel rgbcolor;  /* Color of axes */
-    int i;
-
-    PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
     ppmd_line(pixels, pixcols, pixrows, maxval,
               B(0, 0), B(0, pxrows - 1), PPMD_NULLDRAWPROC,
-              (char *) &rgbcolor);
+              (char *) &axisColor);
+}
+
+
+
+static void
+drawXAxis(pixel **     const pixels,
+          unsigned int const pixcols,
+          unsigned int const pixrows,
+          pixval       const maxval,
+          unsigned int const xBias,
+          unsigned int const yBias,
+          pixel        const axisColor) {
+              
+    unsigned int const pxcols = pixcols - xBias;
+    unsigned int const pxrows = pixrows - yBias;
+
     ppmd_line(pixels, pixcols, pixrows, maxval,
               B(0, pxrows - 1), B(pxcols - 1, pxrows - 1),
-              PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-    
-    /* Draw tick marks on X and Y axes every 0.1 units.  Also
-       label axes.
-    */
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+tickX(pixel **     const pixels,
+      unsigned int const pixcols,
+      unsigned int const pixrows,
+      pixval       const maxval,
+      unsigned int const xBias,
+      unsigned int const yBias,
+      pixel        const axisColor,
+      unsigned int const tenth) {
+/*----------------------------------------------------------------------------
+   Put a tick mark 'tenth' tenths of the way along the X axis
+   and label it.
+
+   'pixels' is the canvas on which to draw it; its dimensions are
+   'pixcols' by 'pixrows' and has maxval 'maxval'.
+-----------------------------------------------------------------------------*/
+    unsigned int const pxcols = pixcols - xBias;
+    unsigned int const pxrows = pixrows - yBias;
+    unsigned int const tickCol = (tenth * (pxcols - 1)) / 10;
+        /* Pixel column where the left edge of the tick goes */
+    unsigned int const tickThickness = Sz(3);
+        /* Thickness of the tick in pixels */
+    unsigned int const tickBottom = pxrows - Sz(1);
+        /* Pixel row of the bottom of the tick */
+
+    char s[20];
+
+    assert(tenth < 10);
+
+    sprintf(s, "0.%u", tenth);
+    ppmd_line(pixels, pixcols, pixrows, maxval,
+              B(tickCol, tickBottom),
+              B(tickCol, tickBottom - tickThickness),
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+    ppmd_text(pixels, pixcols, pixrows, maxval,
+              B(tickCol - Sz(11), pxrows + Sz(12)),
+              Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+tickY(pixel **     const pixels,
+      unsigned int const pixcols,
+      unsigned int const pixrows,
+      pixval       const maxval,
+      unsigned int const xBias,
+      unsigned int const yBias,
+      pixel        const axisColor,
+      unsigned int const tenth) {
+/*----------------------------------------------------------------------------
+   Put a tick mark 'tenth' tenths of the way along the Y axis and label it.
+
+   'pixels' is the canvas on which to draw it; its dimensions are
+   'pixcols' by 'pixrows' and has maxval 'maxval'.
+-----------------------------------------------------------------------------*/
+    unsigned int const pxrows = pixrows - yBias;
+    unsigned int const tickRow = (tenth * (pxrows - 1)) / 10;
+        /* Pixel row where the top of the tick goes */
+    unsigned int const tickThickness = Sz(3);
+        /* Thickness of the tick in pixels */
     
-    for (i = 1; i <= 9; i += 1) {
-        char s[20];
-
-        /* X axis tick */
-
-        sprintf(s, "0.%d", i);
-        ppmd_line(pixels, pixcols, pixrows, maxval,
-                  B((i * (pxcols - 1)) / 10, pxrows - Sz(1)),
-                  B((i * (pxcols - 1)) / 10, pxrows - Sz(4)),
-                  PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-        ppmd_text(pixels, pixcols, pixrows, maxval,
-                  B((i * (pxcols - 1)) / 10 - Sz(11), pxrows + Sz(12)),
-                  Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-
-        /* Y axis tick */
-
-        sprintf(s, "0.%d", 10 - i);
-        ppmd_line(pixels, pixcols, pixrows, maxval,
-                  B(0, (i * (pxrows - 1)) / 10),
-                  B(Sz(3), (i * (pxrows - 1)) / 10),
-                  PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-
-        ppmd_text(pixels, pixcols, pixrows, maxval,
-                  B(Sz(-30), (i * (pxrows - 1)) / 10 + Sz(5)),
-                  Sz(10), 0, s,
-                  PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-    }
+    char s[20];
+
+    assert(tenth < 10);
+
+    sprintf(s, "0.%d", 10 - tenth);
+    ppmd_line(pixels, pixcols, pixrows, maxval,
+              B(0, tickRow), B(tickThickness, tickRow),
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+
+    ppmd_text(pixels, pixcols, pixrows, maxval,
+              B(Sz(-30), tickRow + Sz(5)),
+              Sz(10), 0, s,
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+labelAxes(pixel **     const pixels,
+          unsigned int const pixcols,
+          unsigned int const pixrows,
+          pixval       const maxval,
+          unsigned int const xBias,
+          unsigned int const yBias,
+          pixel        const axisColor,
+          bool         const upvp) {
+/*----------------------------------------------------------------------------
+   Label the axes "x" and "y" or "u" and "v".
+-----------------------------------------------------------------------------*/
+    unsigned int const pxcols = pixcols - xBias;
+    unsigned int const pxrows = pixrows - yBias;
+
     ppmd_text(pixels, pixcols, pixrows, maxval,
               B((98 * (pxcols - 1)) / 100 - Sz(11), pxrows + Sz(12)),
               Sz(10), 0, (upvp ? "u'" : "x"),
-              PPMD_NULLDRAWPROC, (char *) &rgbcolor);
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
     ppmd_text(pixels,  pixcols, pixrows, maxval,
               B(Sz(-22), (2 * (pxrows - 1)) / 100 + Sz(5)),
               Sz(10), 0, (upvp ? "v'" : "y"),
-              PPMD_NULLDRAWPROC, (char *) &rgbcolor);
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+drawAxes(pixel **     const pixels,
+         unsigned int const pixcols,
+         unsigned int const pixrows,
+         pixval       const maxval,
+         bool         const upvp,
+         unsigned int const xBias,
+         unsigned int const yBias) {
+/*----------------------------------------------------------------------------
+   Draw the axes, with tick marks every .1 units and labels.
+-----------------------------------------------------------------------------*/
+    pixel axisColor;  /* Color of axes and labels */
+    unsigned int i;
+
+    PPM_ASSIGN(axisColor, maxval, maxval, maxval);
+
+    drawYAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor);
+    drawXAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor);
+
+    for (i = 1; i <= 9; i += 1) {
+        tickX(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i);
+
+        tickY(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i);
+    }
+
+    labelAxes(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor,
+              upvp);
 }
 
 
@@ -840,14 +957,14 @@ plotBlackBodyCurve(pixel **                   const pixels,
         }
 
         if (t > 1000) {
-            ppmd_line(pixels, pixcols, pixrows, Maxval,
+            ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(lx, ly), B(xb, yb), PPMD_NULLDRAWPROC,
                       (char *) &rgbcolor);
 
             /* Draw tick mark every 1000 kelvins */
 
             if ((((int) t) % 1000) == 0) {
-                ppmd_line(pixels, pixcols, pixrows, Maxval,
+                ppmd_line(pixels, pixcols, pixrows, maxval,
                           B(lx, ly - Sz(2)), B(lx, ly + Sz(2)),
                           PPMD_NULLDRAWPROC, (char *) &rgbcolor);
 
@@ -859,7 +976,7 @@ plotBlackBodyCurve(pixel **                   const pixels,
                     char bb[20];
 
                     sprintf(bb, "%g", t);
-                    ppmd_text(pixels, pixcols, pixrows, Maxval,
+                    ppmd_text(pixels, pixcols, pixrows, maxval,
                               B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb,
                               PPMD_NULLDRAWPROC, (char *) &rgbcolor);
                 }
@@ -938,7 +1055,7 @@ plotMonochromeWavelengths(
             PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
             tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0));
             ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); 
-            ppmd_line(pixels, pixcols, pixrows, Maxval,
+            ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(icx, icy), B(tx, ty),
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
 
@@ -970,13 +1087,13 @@ plotMonochromeWavelengths(
             }
             /* gamma correct from linear rgb to nonlinear rgb. */
             gamma_correct_rgb(cs, &jr, &jg, &jb);
-            r = Maxval * jr;
-            g = Maxval * jg;
-            b = Maxval * jb;
+            r = maxval * jr;
+            g = maxval * jg;
+            b = maxval * jb;
             PPM_ASSIGN(rgbcolor, (pixval) r, (pixval) g, (pixval) b);
 
             sprintf(wl, "%d", x);
-            ppmd_text(pixels, pixcols, pixrows, Maxval,
+            ppmd_text(pixels, pixcols, pixrows, maxval,
                       B(icx + bx, icy + by), Sz(6), 0, wl,
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
         }
@@ -997,18 +1114,18 @@ writeLabel(pixel **                   const pixels,
 
     PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
     
-    snprintfN(sysdesc, sizeof(sysdesc),
-              "System: %s\n"
-              "Primary illuminants (X, Y)\n"
-              "     Red:  %0.4f, %0.4f\n"
-              "     Green: %0.4f, %0.4f\n"
-              "     Blue:  %0.4f, %0.4f\n"
-              "White point (X, Y): %0.4f, %0.4f",
-              cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen,
-              cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite);
+    pm_snprintf(sysdesc, sizeof(sysdesc),
+                "System: %s\n"
+                "Primary illuminants (X, Y)\n"
+                "     Red:  %0.4f, %0.4f\n"
+                "     Green: %0.4f, %0.4f\n"
+                "     Blue:  %0.4f, %0.4f\n"
+                "White point (X, Y): %0.4f, %0.4f",
+                cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen,
+                cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite);
     sysdesc[sizeof(sysdesc)-1] = '\0';  /* for robustness */
 
-    ppmd_text(pixels, pixcols, pixrows, Maxval,
+    ppmd_text(pixels, pixcols, pixrows, maxval,
               pixcols / 3, Sz(24), Sz(12), 0, sysdesc,
               PPMD_NULLDRAWPROC, (char *) &rgbcolor);
 }
@@ -1016,8 +1133,8 @@ writeLabel(pixel **                   const pixels,
 
 
 int
-main(int argc,
-     char * argv[]) {
+main(int          argc,
+     const char * argv[]) {
 
     int argn;
     const char * const usage = "[-[no]black] [-[no]wpoint] [-[no]label] [-no[axes]] [-full]\n\
@@ -1027,24 +1144,24 @@ main(int argc,
 [-size <s>] [-xsize|-width <x>] [-ysize|-height <y>]";
     const struct colorSystem *cs;
 
-    int widspec = FALSE, hgtspec = FALSE;
-    int xBias, yBias;
-    int upvp = FALSE;             /* xy or u'v' color coordinates? */
-    int showWhite = TRUE;             /* Show white point ? */
-    int showBlack = TRUE;             /* Show black body curve ? */
-    int fullChart = FALSE;            /* Fill entire tongue ? */
-    int showLabel = TRUE;             /* Show labels ? */
-    int showAxes = TRUE;              /* Plot axes ? */
+    bool widspec = false, hgtspec = false;
+    unsigned int xBias, yBias;
+    bool upvp = false;             /* xy or u'v' color coordinates? */
+    bool showWhite = true;         /* Show white point ? */
+    bool showBlack = true;         /* Show black body curve ? */
+    bool fullChart = false;        /* Fill entire tongue ? */
+    bool showLabel = true;         /* Show labels ? */
+    bool showAxes = true;          /* Plot axes ? */
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
     argn = 1;
 
     cs = &Rec709system;  /* default */
     while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
         if (pm_keymatch(argv[argn], "-xy", 2)) {
-            upvp = FALSE;
+            upvp = false;
         } else if (pm_keymatch(argv[argn], "-upvp", 1)) {
-            upvp = TRUE;
+            upvp = true;
         } else if (pm_keymatch(argv[argn], "-xsize", 1) ||
                    pm_keymatch(argv[argn], "-width", 2)) {
             if (widspec) {
@@ -1053,7 +1170,7 @@ main(int argc,
             argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1))
                 pm_usage(usage);
-            widspec = TRUE;
+            widspec = true;
         } else if (pm_keymatch(argv[argn], "-ysize", 1) ||
                    pm_keymatch(argv[argn], "-height", 2)) {
             if (hgtspec) {
@@ -1062,7 +1179,7 @@ main(int argc,
             argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
                 pm_usage(usage);
-            hgtspec = TRUE;
+            hgtspec = true;
         } else if (pm_keymatch(argv[argn], "-size", 2)) {
             if (hgtspec || widspec) {
                 pm_error("already specified a size/height/ysize");
@@ -1071,7 +1188,7 @@ main(int argc,
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
                 pm_usage(usage);
             sxsize = sysize;
-            hgtspec = widspec = TRUE;
+            hgtspec = widspec = true;
         } else if (pm_keymatch(argv[argn], "-rec709", 1)) {
             cs = &Rec709system;
         } else if (pm_keymatch(argv[argn], "-ntsc", 1)) {
@@ -1085,23 +1202,23 @@ main(int argc,
         } else if (pm_keymatch(argv[argn], "-cie", 1)) {
             cs = &CIEsystem;                 
         } else if (pm_keymatch(argv[argn], "-black", 3)) {
-            showBlack = TRUE;         /* Show black body curve */
+            showBlack = true;         /* Show black body curve */
         } else if (pm_keymatch(argv[argn], "-wpoint", 2)) {
-            showWhite = TRUE;         /* Show white point of color system */
+            showWhite = true;         /* Show white point of color system */
         } else if (pm_keymatch(argv[argn], "-noblack", 3)) {
-            showBlack = FALSE;        /* Don't show black body curve */
+            showBlack = false;        /* Don't show black body curve */
         } else if (pm_keymatch(argv[argn], "-nowpoint", 3)) {
-            showWhite = FALSE;        /* Don't show white point of system */
+            showWhite = false;        /* Don't show white point of system */
         } else if (pm_keymatch(argv[argn], "-label", 1)) {
-            showLabel = TRUE;         /* Show labels. */
+            showLabel = true;         /* Show labels. */
         } else if (pm_keymatch(argv[argn], "-nolabel", 3)) {
-            showLabel = FALSE;        /* Don't show labels */
+            showLabel = false;        /* Don't show labels */
         } else if (pm_keymatch(argv[argn], "-axes", 1)) {
-            showAxes = TRUE;          /* Show axes. */
+            showAxes = true;          /* Show axes. */
         } else if (pm_keymatch(argv[argn], "-noaxes", 3)) {
-            showAxes = FALSE;         /* Don't show axes */
+            showAxes = false;         /* Don't show axes */
         } else if (pm_keymatch(argv[argn], "-full", 1)) {
-            fullChart = TRUE;         /* Fill whole tongue full-intensity */
+            fullChart = true;         /* Fill whole tongue full-intensity */
         } else if (pm_keymatch(argv[argn], "-gamma", 2)) {
             cs = &Customsystem;
             argn++;
@@ -1170,32 +1287,32 @@ main(int argc,
 
     makeAllBlack(pixels, pixcols, pixrows);
 
-    drawTongueOutline(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias);
+    drawTongueOutline(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias);
 
-    fillInTongue(pixels, pixcols, pixrows, Maxval, cs, upvp, xBias, yBias,
+    fillInTongue(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias,
                  fullChart);
 
     if (showAxes)
-        drawAxes(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias);
+        drawAxes(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias);
 
     if (showWhite)
-        plotWhitePoint(pixels, pixcols, pixrows, Maxval,
+        plotWhitePoint(pixels, pixcols, pixrows, cieMaxval,
                        cs, upvp, xBias, yBias);
 
     if (showBlack)
-        plotBlackBodyCurve(pixels, pixcols, pixrows, Maxval,
+        plotBlackBodyCurve(pixels, pixcols, pixrows, cieMaxval,
                            upvp, xBias, yBias);
 
     /* Plot wavelengths around periphery of the tongue. */
 
     if (showAxes)
-        plotMonochromeWavelengths(pixels, pixcols, pixrows, Maxval,
+        plotMonochromeWavelengths(pixels, pixcols, pixrows, cieMaxval,
                                   cs, upvp, xBias, yBias);
 
     if (showLabel)
-        writeLabel(pixels, pixcols, pixrows, Maxval, cs);
+        writeLabel(pixels, pixcols, pixrows, cieMaxval, cs);
 
-    ppm_writeppm(stdout, pixels, pixcols, pixrows, Maxval, FALSE);
+    ppm_writeppm(stdout, pixels, pixcols, pixrows, cieMaxval, 0);
 
     return 0;
 }
diff --git a/generator/ppmcolors.c b/generator/ppmcolors.c
index ecae2285..701812d1 100644
--- a/generator/ppmcolors.c
+++ b/generator/ppmcolors.c
@@ -45,7 +45,7 @@ parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) {
     /* defaults */
     cmdlineP->maxval = 5;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->maxval < 1)
@@ -72,15 +72,16 @@ main(int argc, char *argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
-    asprintfN(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval);
+    pm_asprintf(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval);
 
     rc = system(cmd);
 
     if (rc != 0) 
         pm_error("pamseq|pamtopnm pipeline failed.  system() rc = %d", rc);
 
-    strfree(cmd);
-    exit(rc);
+    pm_strfree(cmd);
+
+    return rc;
 }
 
 
diff --git a/generator/ppmforge.c b/generator/ppmforge.c
index c77076e3..8ea86429 100644
--- a/generator/ppmforge.c
+++ b/generator/ppmforge.c
@@ -39,12 +39,9 @@
 #include "pm_c_util.h"
 #include "ppm.h"
 #include "mallocvar.h"
+#include "shhopt.h"
 
-#ifdef VMS
-static double const hugeVal = HUGE_VAL;
-#else
 static double const hugeVal = 1e50;
-#endif
 
 /* Definitions used to address real and imaginary parts in a two-dimensional
    array of complex numbers as stored by fourn(). */
@@ -78,12 +75,6 @@ static double arand, gaussadd, gaussfac; /* Gaussian random parameters */
 static double fracdim;            /* Fractal dimension */
 static double powscale;           /* Power law scaling exponent */
 static int meshsize = 256;        /* FFT mesh size */
-static unsigned int seedarg;        /* Seed specified by user */
-static bool seedspec = FALSE;      /* Did the user specify a seed ? */
-static bool clouds = FALSE;        /* Just generate clouds */
-static bool stars = FALSE;         /* Just generate stars */
-static int screenxsize = 256;         /* Screen X size */
-static int screenysize = 256;         /* Screen Y size */
 static double inclangle, hourangle;   /* Star position relative to planet */
 static bool inclspec = FALSE;      /* No inclination specified yet */
 static bool hourspec = FALSE;      /* No hour specified yet */
@@ -92,6 +83,166 @@ static double glaciers;           /* Glacier level */
 static int starfraction;          /* Star fraction */
 static int starcolor;            /* Star color saturation */
 
+
+struct CmdlineInfo {
+    unsigned int clouds;
+    unsigned int night;
+    float        dimension;
+    float        hourAngle;
+    unsigned int hourSpec;
+    float        inclAngle;
+    unsigned int inclinationSpec;
+    unsigned int meshSize;
+    unsigned int meshSpec;
+    float        power;
+    float        glaciers;
+    float        ice;
+    int          saturation;
+    unsigned int seed;
+    int          stars;
+    unsigned int starsSpec;
+    unsigned int width;
+    unsigned int height;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char **argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+  Convert program invocation arguments (argc,argv) into a format the 
+  program can use easily, struct cmdlineInfo.  Validate arguments along
+  the way and exit program with message if invalid.
+
+  Note that some string information we return as *cmdlineP is in the storage 
+  argv[] points to.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int dimensionSpec, seedSpec,
+        meshSpec, powerSpec, glaciersSpec, iceSpec, saturationSpec,
+        starsSpec, widthSpec, heightSpec;
+    float hour;
+    float inclination;
+    unsigned int mesh;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;
+    OPTENT3(0, "clouds",      OPT_FLAG, NULL, &cmdlineP->clouds, 0);
+    OPTENT3(0, "night",       OPT_FLAG, NULL, &cmdlineP->night, 0);
+    OPTENT3(0, "dimension",   OPT_FLOAT, &cmdlineP->dimension,
+            &dimensionSpec, 0);
+    OPTENT3(0, "hour",        OPT_FLOAT, &hour,
+            &cmdlineP->hourSpec, 0);
+    OPTENT3(0, "inclination", OPT_FLOAT, &inclination,
+            &cmdlineP->inclinationSpec, 0);
+    OPTENT3(0, "tilt",        OPT_FLOAT, &inclination,
+            &cmdlineP->inclinationSpec, 0);
+    OPTENT3(0, "mesh",        OPT_UINT, &mesh,
+            &meshSpec, 0);
+    OPTENT3(0, "power",       OPT_FLOAT, &cmdlineP->power,
+            &powerSpec, 0);
+    OPTENT3(0, "glaciers",    OPT_FLOAT, &cmdlineP->glaciers,
+            &glaciersSpec, 0);
+    OPTENT3(0, "ice",         OPT_FLOAT, &cmdlineP->ice,
+            &iceSpec, 0);
+    OPTENT3(0, "saturation",  OPT_INT,   &cmdlineP->saturation,
+            &saturationSpec, 0);
+    OPTENT3(0, "seed",        OPT_UINT,  &cmdlineP->seed,
+            &seedSpec, 0);
+    OPTENT3(0, "stars",       OPT_INT,   &cmdlineP->stars,
+            &starsSpec, 0);
+    OPTENT3(0, "width",       OPT_UINT,  &cmdlineP->width,
+            &widthSpec, 0);
+    OPTENT3(0, "xsize",       OPT_UINT,  &cmdlineP->width,
+            &widthSpec, 0);
+    OPTENT3(0, "height",      OPT_UINT,  &cmdlineP->height,
+            &heightSpec, 0);
+    OPTENT3(0, "ysize",       OPT_UINT,  &cmdlineP->height,
+            &heightSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (dimensionSpec) {
+        if (cmdlineP->dimension <= 0.0)
+            pm_error("-dimension must be greater than zero.  "
+                     "You specified %f", cmdlineP->dimension);
+    } else
+        cmdlineP->dimension = cmdlineP->clouds ? 2.15 : 2.4;
+
+    if (cmdlineP->hourSpec)
+        cmdlineP->hourAngle = (M_PI / 12.0) * (hour + 12.0);
+
+    if (cmdlineP->inclinationSpec)
+        cmdlineP->inclAngle = (M_PI / 180.0) * inclination;
+
+    if (meshSpec) {
+        unsigned int i;
+        if (mesh < 2)
+            pm_error("-mesh value must be at least 2.  "
+                     "You specified %u", mesh);
+        /* Force FFT mesh to the next larger power of 2. */
+        for (i = 2; i < mesh; i <<= 1);
+        cmdlineP->meshSize = i;
+    } else
+        cmdlineP->meshSize = 256;
+
+    if (powerSpec) {
+        if (cmdlineP->power <= 0.0)
+            pm_error("-power must be greater than zero.  "
+                     "You specified %f", cmdlineP->power);
+    } else
+        cmdlineP->power = cmdlineP->clouds ? 0.75 : 1.2;
+
+    if (iceSpec) {
+        if (cmdlineP->ice <= 0.0)
+            pm_error("-ice must be greater than zero.  "
+                     "You specified %f", cmdlineP->ice);
+    } else
+        cmdlineP->ice = 0.4;
+
+    if (glaciersSpec) {
+        if (cmdlineP->glaciers <= 0.0)
+            pm_error("-glaciers must be greater than 0.  "
+                     "You specified %f", cmdlineP->glaciers);
+    } else
+        cmdlineP->glaciers = 0.75;
+
+    if (!starsSpec)
+        cmdlineP->stars = 100;
+
+    if (!saturationSpec)
+        cmdlineP->saturation = 125;
+
+    if (!seedSpec)
+        cmdlineP->seed = pm_randseed();
+
+    if (!widthSpec)
+        cmdlineP->width = 256;
+
+    if (!heightSpec)
+        cmdlineP->height = 256;
+
+    if (argc-1 > 0)
+        pm_error("There are no non-option arguments.  "
+                 "You specified %u", argc-1);
+
+    free(option_def);
+}
+
+
 /*  FOURN  --  Multi-dimensional fast Fourier transform
 
     Called with arguments:
@@ -278,19 +429,6 @@ static void spectralsynth(x, n, h)
 }
 
 
-static unsigned int
-initseed(void) {
-    /*  Generate initial random seed.  */
-
-    unsigned int i;
-
-    srand(pm_randseed());
-    for (i = 0; i < 7; ++i)
-        rand();
-    return rand();
-}
-
-
 
 /*  TEMPRGB  --  Calculate the relative R, G, and B components  for  a
          black  body  emitting  light  at a given temperature.
@@ -421,7 +559,8 @@ makeCp(float *      const a,
 
 
 static void
-createPlanetStuff(float *          const a,
+createPlanetStuff(bool             const clouds,
+                  float *          const a,
                   unsigned int     const n,
                   double **        const uP,
                   double **        const u1P,
@@ -460,7 +599,9 @@ createPlanetStuff(float *          const a,
             "        -inclination %.0f -hour %d -ice %.2f -glaciers %.2f",
             (siang * (180.0 / M_PI)),
             (int) (((shang * (12.0 / M_PI)) + 12 +
-                    (flipped ? 12 : 0)) + 0.5) % 24, icelevel, glaciers);
+                    (flipped ? 12 : 0)) + 0.5) % 24,
+            icelevel,
+            glaciers);
         pm_message("        -stars %d -saturation %d.",
                    starfraction, starcolor);
     }
@@ -535,9 +676,9 @@ generateCloudRow(pixel *         const pixels,
 
     /* Render the FFT output as clouds. */
 
-    unsigned int j;
+    unsigned int col;
 
-    for (j = 0; j < cols; j++) {
+    for (col = 0; col < cols; ++col) {
         double r;
         pixval w;
         
@@ -546,15 +687,15 @@ generateCloudRow(pixel *         const pixels,
            referenced below does not exist.
         */
         if (t1 > 0.0)
-            r += t1 * u1[j] * cp[byf + bxf[j]] +
-                t1 * u[j]  * cp[byf + bxc[j]];
+            r += t1 * u1[col] * cp[byf + bxf[col]] +
+                t1 * u[col]  * cp[byf + bxc[col]];
         if (t > 0.0)
-            r += t * u1[j] * cp[byc + bxf[j]] +
-                t * u[j]  * cp[byc + bxc[j]];
+            r += t * u1[col] * cp[byc + bxf[col]] +
+                t * u[col]  * cp[byc + bxc[col]];
         
         w = (r > 127.0) ? (maxval * ((r - 127.0) / 128.0)) : 0;
         
-        PPM_ASSIGN(*(pixels + j), w, w, maxval);
+        PPM_ASSIGN(pixels[col], w, w, maxval);
     }
 }
 
@@ -823,7 +964,7 @@ genplanet(bool         const stars,
         pm_message("%s: -seed %d -dimension %.2f -power %.2f -mesh %d",
                    clouds ? "clouds" : "planet",
                    rseed, fracdim, powscale, meshsize);
-        createPlanetStuff(a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, 
+        createPlanetStuff(clouds, a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, 
                           cols, maxval);
     }
 
@@ -944,19 +1085,14 @@ static bool
 planet(unsigned int const cols,
        unsigned int const rows,
        bool         const stars,
-       bool         const clouds) {
+       bool         const clouds,
+       unsigned int const rseed) {
 /*----------------------------------------------------------------------------
    Make a planet.
 -----------------------------------------------------------------------------*/
     float * a;
     bool error;
-    unsigned int rseed;        /* Current random seed */
-    
-    if (seedspec)
-        rseed = seedarg;
-    else 
-        rseed = initseed();
-    
+
     initgauss(rseed);
     
     if (stars) {
@@ -985,200 +1121,39 @@ planet(unsigned int const cols,
 
 
 
-
 int 
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
+    struct CmdlineInfo cmdline;
     bool success;
-    int i;
-    const char * const usage = "\n\
-[-width|-xsize <x>] [-height|-ysize <y>] [-mesh <n>]\n\
-[-clouds] [-dimension <f>] [-power <f>] [-seed <n>]\n\
-[-hour <f>] [-inclination|-tilt <f>] [-ice <f>] [-glaciers <f>]\n\
-[-night] [-stars <n>] [-saturation <n>]";
-    bool dimspec = FALSE, meshspec = FALSE, powerspec = FALSE,
-        widspec = FALSE, hgtspec = FALSE, icespec = FALSE,
-        glacspec = FALSE, starspec = FALSE, starcspec = FALSE;
-
-    int cols, rows;     /* Dimensions of our output image */
-
-    ppm_init(&argc, argv);
-    i = 1;
-    
-    while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) {
-
-        if (pm_keymatch(argv[i], "-clouds", 2)) {
-            clouds = TRUE;
-        } else if (pm_keymatch(argv[i], "-night", 2)) {
-            stars = TRUE;
-        } else if (pm_keymatch(argv[i], "-dimension", 2)) {
-            if (dimspec) {
-                pm_error("already specified a dimension");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &fracdim)  != 1))
-                pm_usage(usage);
-            if (fracdim <= 0.0) {
-                pm_error("fractal dimension must be greater than 0");
-            }
-            dimspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-hour", 3)) {
-            if (hourspec) {
-                pm_error("already specified an hour");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &hourangle) != 1))
-                pm_usage(usage);
-            hourangle = (M_PI / 12.0) * (hourangle + 12.0);
-            hourspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-inclination", 3) ||
-                   pm_keymatch(argv[i], "-tilt", 2)) {
-            if (inclspec) {
-                pm_error("already specified an inclination/tilt");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &inclangle) != 1))
-                pm_usage(usage);
-            inclangle = (M_PI / 180.0) * inclangle;
-            inclspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-mesh", 2)) {
-            unsigned int j;
 
-            if (meshspec) {
-                pm_error("already specified a mesh size");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &meshsize) != 1))
-                pm_usage(usage);
+    unsigned int cols, rows;     /* Dimensions of our output image */
 
-            if (meshsize < 2)
-                pm_error("mesh must be at least 2");
+    pm_proginit(&argc, argv);
 
-            /* Force FFT mesh to the next larger power of 2. */
+    parseCommandLine(argc, argv, &cmdline);
 
-            for (j = meshsize; (j & 1) == 0; j >>= 1) ;
-
-            if (j != 1) {
-                for (j = 2; j < meshsize; j <<= 1) ;
-                meshsize = j;
-            }
-            meshspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-power", 2)) {
-            if (powerspec) {
-                pm_error("already specified a power factor");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &powscale) != 1))
-                pm_usage(usage);
-            if (powscale <= 0.0) {
-                pm_error("power factor must be greater than 0");
-            }
-            powerspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-ice", 3)) {
-            if (icespec) {
-                pm_error("already specified ice cap level");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &icelevel) != 1))
-                pm_usage(usage);
-            if (icelevel <= 0.0) {
-                pm_error("ice cap level must be greater than 0");
-            }
-            icespec = TRUE;
-        } else if (pm_keymatch(argv[i], "-glaciers", 2)) {
-            if (glacspec) {
-                pm_error("already specified glacier level");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &glaciers) != 1))
-                pm_usage(usage);
-            if (glaciers <= 0.0) {
-                pm_error("glacier level must be greater than 0");
-            }
-            glacspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-stars", 3)) {
-            if (starspec) {
-                pm_error("already specified a star fraction");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &starfraction) != 1))
-                pm_usage(usage);
-            starspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-saturation", 3)) {
-            if (starcspec) {
-                pm_error("already specified a star color saturation");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &starcolor) != 1))
-                pm_usage(usage);
-            starcspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-seed", 3)) {
-            if (seedspec) {
-                pm_error("already specified a random seed");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%u", &seedarg) != 1))
-                pm_usage(usage);
-            seedspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-xsize", 2) ||
-                   pm_keymatch(argv[i], "-width", 2)) {
-            if (widspec) {
-                pm_error("already specified a width/xsize");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1))
-                pm_usage(usage);
-            widspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-ysize", 2) ||
-                   pm_keymatch(argv[i], "-height", 3)) {
-            if (hgtspec) {
-                pm_error("already specified a height/ysize");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1))
-                pm_usage(usage);
-            hgtspec = TRUE;
-        } else {
-            pm_usage(usage);
-        }
-        i++;
-    }
-
-
-    /* Set defaults when explicit specifications were not given.
-
-       The  default  fractal  dimension  and  power  scale depend upon
-       whether we are generating a planet or clouds. 
-    */
-    
-    if (!dimspec) {
-        fracdim = clouds ? 2.15 : 2.4;
-    }
-    if (!powerspec) {
-        powscale = clouds ? 0.75 : 1.2;
-    }
-    if (!icespec) {
-        icelevel = 0.4;
-    }
-    if (!glacspec) {
-        glaciers = 0.75;
-    }
-    if (!starspec) {
-        starfraction = 100;
-    }
-    if (!starcspec) {
-        starcolor = 125;
-    }
+    fracdim      = cmdline.dimension;
+    hourspec     = cmdline.hourSpec;
+    hourangle    = cmdline.hourAngle;
+    inclspec     = cmdline.inclinationSpec;
+    inclangle    = cmdline.inclAngle;
+    meshsize     = cmdline.meshSize;
+    powscale     = cmdline.power;
+    icelevel     = cmdline.ice;
+    glaciers     = cmdline.glaciers;
+    starfraction = cmdline.stars;
+    starcolor    = cmdline.saturation;
 
     /* Force  screen to be at least  as wide as it is high.  Long,
        skinny screens  cause  crashes  because  picture  width  is
        calculated based on height.  
     */
 
-    cols = (MAX(screenysize, screenxsize) + 1) & (~1);
-    rows = screenysize;
+    cols = (MAX(cmdline.height, cmdline.width) + 1) & (~1);
+    rows = cmdline.height;
 
-    success = planet(cols, rows, stars, clouds);
+    success = planet(cols, rows, cmdline.night, cmdline.clouds, cmdline.seed);
 
     exit(success ? 0 : 1);
 }
diff --git a/generator/ppmmake.c b/generator/ppmmake.c
index e59b47d5..2d4bbca8 100644
--- a/generator/ppmmake.c
+++ b/generator/ppmmake.c
@@ -55,19 +55,21 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = false;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free (option_def);
+
     if (!maxvalSpec)
         cmdlineP->maxval = PPM_MAXMAXVAL;
     else {
         if (cmdlineP->maxval > PPM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PPM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 3)
         pm_error("Need 3 arguments: color, width, height.");
@@ -88,7 +90,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     pixel * pixrow;
-    unsigned int row;
+    unsigned int row, col;
 
     ppm_init(&argc, argv);
 
@@ -97,12 +99,12 @@ main(int argc, char *argv[]) {
     ppm_writeppminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     pixrow = ppm_allocrow(cmdline.cols);
 
-    for (row = 0; row < cmdline.rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cmdline.cols; ++col)
-            pixrow[col] = cmdline.color;
+    /* All rows are identical.  Fill once. */
+    for (col = 0; col < cmdline.cols; ++col)
+        pixrow[col] = cmdline.color;
+
+    for (row = 0; row < cmdline.rows; ++row)
         ppm_writeppmrow(stdout, pixrow, cmdline.cols, cmdline.maxval, 0);
-	}
 
     ppm_freerow(pixrow);
     pm_close(stdout);
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 772fa51d..fe1a1d27 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -41,6 +41,8 @@ struct cmdlineInfo {
     pattern basePattern;
     unsigned int width;
     unsigned int height;
+    unsigned int randomseed;
+    unsigned int randomseedSpec;
 };
 
 
@@ -71,22 +73,34 @@ parseCommandLine(int argc, const char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "gingham2",  OPT_FLAG,   NULL, &gingham2,   0);
-    OPTENT3(0, "g2",        OPT_FLAG,   NULL, &gingham2,   0);
-    OPTENT3(0, "gingham3",  OPT_FLAG,   NULL, &gingham3,   0);
-    OPTENT3(0, "g3",        OPT_FLAG,   NULL, &gingham3,   0);
-    OPTENT3(0, "madras",    OPT_FLAG,   NULL, &madras,     0);
-    OPTENT3(0, "tartan",    OPT_FLAG,   NULL, &tartan,     0);
-    OPTENT3(0, "poles",     OPT_FLAG,   NULL, &poles,      0);
-    OPTENT3(0, "squig",     OPT_FLAG,   NULL, &squig,      0);
-    OPTENT3(0, "camo",      OPT_FLAG,   NULL, &camo,       0);
-    OPTENT3(0, "anticamo",  OPT_FLAG,   NULL, &anticamo,   0);
+    OPTENT3(0, "gingham2",      OPT_FLAG,   NULL,
+            &gingham2,   0);
+    OPTENT3(0, "g2",            OPT_FLAG,   NULL,
+            &gingham2,   0);
+    OPTENT3(0, "gingham3",      OPT_FLAG,   NULL,
+            &gingham3,   0);
+    OPTENT3(0, "g3",            OPT_FLAG,   NULL,
+            &gingham3,   0);
+    OPTENT3(0, "madras",        OPT_FLAG,   NULL,
+            &madras,     0);
+    OPTENT3(0, "tartan",        OPT_FLAG,   NULL,
+            &tartan,     0);
+    OPTENT3(0, "poles",         OPT_FLAG,   NULL,
+            &poles,      0);
+    OPTENT3(0, "squig",         OPT_FLAG,   NULL,
+            &squig,      0);
+    OPTENT3(0, "camo",          OPT_FLAG,   NULL,
+            &camo,       0);
+    OPTENT3(0, "anticamo",      OPT_FLAG,   NULL,
+            &anticamo,   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 have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     basePatternCount =
@@ -136,6 +150,7 @@ parseCommandLine(int argc, const char ** argv,
         if (cmdlineP->height < 1)
             pm_error("Height must be at least 1 pixel");
     }
+    free(option_def);
 }
 
 
@@ -1140,7 +1155,8 @@ main(int argc, const char ** argv) {
 
     validateComputableDimensions(cmdline.width, cmdline.height);
     
-    srand(pm_randseed());
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
     pixels = ppm_allocarray(cmdline.width, cmdline.height);
 
     switch (cmdline.basePattern) {
@@ -1188,3 +1204,5 @@ main(int argc, const char ** argv) {
     return 0;
 }
 
+
+
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index 96e304ac..c0568d9b 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -1,4 +1,28 @@
-#!/usr/bin/perl -wl
+#!/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
 use strict;
 use Getopt::Long;
 
diff --git a/generator/ppmrough.c b/generator/ppmrough.c
index b21adedf..e749c9c2 100644
--- a/generator/ppmrough.c
+++ b/generator/ppmrough.c
@@ -15,6 +15,7 @@
 #include <sys/time.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "ppm.h"
 
@@ -22,274 +23,309 @@ static pixel** PIX;
 static pixval BG_RED, BG_GREEN, BG_BLUE;
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
   /* All the information the user supplied in the command line,
      in a form easy for the program to use.
   */
-  unsigned int left, right, top, bottom;
-  unsigned int width, height, var;
-  const char *bg_rgb;
-  const char *fg_rgb;
-  unsigned init;
-  unsigned int verbose;
+    unsigned int left, right, top, bottom;
+    unsigned int width, height, var;
+    const char * bg_rgb;
+    const char * fg_rgb;
+    unsigned int randomseed;
+    unsigned int randomseedSpec;
+    unsigned int verbose;
 };
 
 
 static void
-/*-------------------------------------------------------------------------- */
-   parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP)
-/*-------------------------------------------------------------------------- */
-{
-  optEntry *option_def = malloc(100*sizeof(optEntry));
-    /* Instructions to OptParseOptions2 on how to parse our options.    */
-  optStruct3 opt;
-
-  unsigned int option_def_index;
-
-  option_def_index = 0;   /* incremented by OPTENTRY */
-  OPTENT3(0, "width",   OPT_UINT,   &cmdlineP->width,   NULL, 0);
-  OPTENT3(0, "height",  OPT_UINT,   &cmdlineP->height,  NULL, 0);
-  OPTENT3(0, "left",    OPT_UINT,   &cmdlineP->left,    NULL, 0);
-  OPTENT3(0, "right",   OPT_UINT,   &cmdlineP->right,   NULL, 0);
-  OPTENT3(0, "top",     OPT_UINT,   &cmdlineP->top,     NULL, 0);
-  OPTENT3(0, "bottom",  OPT_UINT,   &cmdlineP->bottom,  NULL, 0);
-  OPTENT3(0, "bg",      OPT_STRING, &cmdlineP->bg_rgb,  NULL, 0);
-  OPTENT3(0, "fg",      OPT_STRING, &cmdlineP->fg_rgb,  NULL, 0);
-  OPTENT3(0, "var",     OPT_UINT,   &cmdlineP->var,     NULL, 0);
-  OPTENT3(0, "init",    OPT_UINT,   &cmdlineP->init,    NULL, 0);
-  OPTENT3(0, "verbose", OPT_FLAG,   NULL, &cmdlineP->verbose, 0);
-
-  /* Set the defaults */
-  cmdlineP->width = 100;
-  cmdlineP->height = 100;
-  cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1;
-  cmdlineP->bg_rgb = NULL;
-  cmdlineP->fg_rgb = NULL;
-  cmdlineP->var = 10;
-
-  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 */
-
-  optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-
-  if (argc-1 != 0)
-    pm_error("There are no arguments.  You specified %d.", argc-1);
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.    */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "width",       OPT_UINT,   &cmdlineP->width,   NULL, 0);
+    OPTENT3(0, "height",      OPT_UINT,   &cmdlineP->height,  NULL, 0);
+    OPTENT3(0, "left",        OPT_UINT,   &cmdlineP->left,    NULL, 0);
+    OPTENT3(0, "right",       OPT_UINT,   &cmdlineP->right,   NULL, 0);
+    OPTENT3(0, "top",         OPT_UINT,   &cmdlineP->top,     NULL, 0);
+    OPTENT3(0, "bottom",      OPT_UINT,   &cmdlineP->bottom,  NULL, 0);
+    OPTENT3(0, "bg",          OPT_STRING, &cmdlineP->bg_rgb,  NULL, 0);
+    OPTENT3(0, "fg",          OPT_STRING, &cmdlineP->fg_rgb,  NULL, 0);
+    OPTENT3(0, "var",         OPT_UINT,   &cmdlineP->var,     NULL, 0);
+    OPTENT3(0, "randomseed",  OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "init",        OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose, 0);
+
+    /* Set the defaults */
+    cmdlineP->width = 100;
+    cmdlineP->height = 100;
+    cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1;
+    cmdlineP->bg_rgb = NULL;
+    cmdlineP->fg_rgb = NULL;
+    cmdlineP->var = 10;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (argc-1 != 0)
+        pm_error("There are no arguments.  You specified %d.", argc-1);
+
+    free(option_def);
 }
 
+
+
 static void
-/* ----------------------------------------- */
-   proc_left(int const r1, int const r2, int const c1, int const c2, 
-             unsigned int const var)
-/* ----------------------------------------- */
-{
-  int cm, rm, c;
-
-  if (r1 + 1 == r2)  return;
-  rm = (r1 + r2) >> 1;
-  cm = (c1 + c2) >> 1;
-  cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (c = 0; c < cm; c++)
-    PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_left(r1, rm, c1, cm, var);
-  proc_left(rm, r2, cm, c2, var);
+procLeft(int          const r1,
+         int          const r2,
+         int          const c1,
+         int          const c2, 
+         unsigned int const var) {
+
+    int cm, rm, c;
+
+    if (r1 + 1 == r2)  return;
+    rm = (r1 + r2) >> 1;
+    cm = (c1 + c2) >> 1;
+    cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (c = 0; c < cm; c++)
+        PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
+
+    procLeft(r1, rm, c1, cm, var);
+    procLeft(rm, r2, cm, c2, var);
 }
 
+
+
 static void
-/* ------------------------------------------ */
-   proc_right(int const r1, int const r2, int const c1, int const c2,
-              unsigned int const width, unsigned int const var)
-/* ------------------------------------------ */
-{
-  int cm, rm, c;
-
-  if (r1 + 1 == r2)  return;
-  rm = (r1 + r2) >> 1;
-  cm = (c1 + c2) >> 1;
-  cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (c = cm; c < width; c++)
-    PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_right(r1, rm, c1, cm, width, var);
-  proc_right(rm, r2, cm, c2, width, var);
+procRight(int          const r1,
+          int          const r2,
+          int          const c1,
+          int          const c2,
+          unsigned int const width,
+          unsigned int const var) {
+
+    int cm, rm, c;
+
+    if (r1 + 1 == r2)  return;
+    rm = (r1 + r2) >> 1;
+    cm = (c1 + c2) >> 1;
+    cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (c = cm; c < width; c++)
+        PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
+
+    procRight(r1, rm, c1, cm, width, var);
+    procRight(rm, r2, cm, c2, width, var);
 }
 
+
+
 static void
-/* ---------------------------------------- */
-   proc_top(int const c1, int const c2, int const r1, int const r2,
-            unsigned int const var)
-/* ---------------------------------------- */
-{
-  int rm, cm, r;
-
-  if (c1 + 1 == c2)  return;
-  cm = (c1 + c2) >> 1;
-  rm = (r1 + r2) >> 1;
-  rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (r = 0; r < rm; r++)
-    PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_top(c1, cm, r1, rm, var);
-  proc_top(cm, c2, rm, r2, var);
+procTop(int          const c1,
+        int          const c2,
+        int          const r1,
+        int          const r2,
+        unsigned int const var) {
+
+    int rm, cm, r;
+
+    if (c1 + 1 == c2)  return;
+    cm = (c1 + c2) >> 1;
+    rm = (r1 + r2) >> 1;
+    rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (r = 0; r < rm; r++)
+        PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
+
+    procTop(c1, cm, r1, rm, var);
+    procTop(cm, c2, rm, r2, var);
 }
 
+
+
 static void
-/* ------------------------------------------- */
-   proc_bottom(int const c1, int const c2, int const r1, int const r2,
-               unsigned int const height, unsigned int const var)
-/* ------------------------------------------- */
-{
-  int rm, cm, r;
-
-  if (c1 + 1 == c2)  return;
-  cm = (c1 + c2) >> 1;
-  rm = (r1 + r2) >> 1;
-  rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (r = rm; r < height; r++)
-    PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_bottom(c1, cm, r1, rm, height, var);
-  proc_bottom(cm, c2, rm, r2, height, var);
+procBottom(int          const c1,
+           int          const c2,
+           int          const r1,
+           int          const r2,
+           unsigned int const height,
+           unsigned int const var) {
+
+    int rm, cm, r;
+
+    if (c1 + 1 == c2)  return;
+    cm = (c1 + c2) >> 1;
+    rm = (r1 + r2) >> 1;
+    rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (r = rm; r < height; r++)
+        PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
+
+    procBottom(c1, cm, r1, rm, height, var);
+    procBottom(cm, c2, rm, r2, height, var);
 }
 
 
+
 int
-/* ============================ */
-   main(int argc, char* argv[])
-/* ============================ */
-{
-  struct cmdlineInfo cmdline;
-  pixel bgcolor, fgcolor;
-  pixval fg_red, fg_green, fg_blue;
-  int rows, cols, row;
-  int left, right, top, bottom;
-  int left_r1, left_r2, left_c1, left_c2;
-  int right_r1, right_r2, right_c1, right_c2;
-  int top_r1, top_r2, top_c1, top_c2;
-  int bottom_r1, bottom_r2, bottom_c1, bottom_c2;
-  unsigned init;
-  struct timeval tv;
-  int col;
-
-  ppm_init(&argc, argv);
-
-  parseCommandLine(argc, argv, &cmdline);
-
-  init = cmdline.init;
-  if (init == 0) {
-    gettimeofday(&tv, NULL);
-    srand((unsigned int)tv.tv_usec);
-  }
-  else
-    srand(init);
-
-  cols = cmdline.width;
-  rows = cmdline.height;
-  left = cmdline.left;
-  right = cmdline.right;
-  top = cmdline.top;
-  bottom = cmdline.bottom;
-
-  if (cmdline.bg_rgb)
-    bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL);
-  else
-    PPM_ASSIGN(bgcolor, 0, 0, 0);
-  BG_RED = PPM_GETR(bgcolor);
-  BG_GREEN = PPM_GETG(bgcolor);
-  BG_BLUE = PPM_GETB(bgcolor);
-
-  if (cmdline.fg_rgb)
-    fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL);
-  else
-    PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL);
-  fg_red = PPM_GETR(fgcolor);
-  fg_green = PPM_GETG(fgcolor);
-  fg_blue = PPM_GETB(fgcolor);
-
-  if (cmdline.verbose) {
-    pm_message("width is %d, height is %d, variance is %d.", 
-               cols, rows, cmdline.var);
-    if (left >= 0) pm_message("ragged left border is required");
-    if (right >= 0) pm_message("ragged right border is required");
-    if (top >= 0) pm_message("ragged top border is required");
-    if (bottom >= 0) pm_message("ragged bottom border is required");
-    pm_message("background is %s", ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1));
-    pm_message("foreground is %s", ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1));
-    if (init >= 0) pm_message("srand() initialized with seed %u", init);
-  }
-
-  /* Allocate memory for the whole pixmap */
-  PIX = ppm_allocarray(cols, rows);
-
-  /* First, set all pixel to foreground color */
-  for (row = 0; row < rows; row++)
-    for (col = 0; col < cols; col++)
-      PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue);
-
-  /* Make a ragged left border */
-  if (left >= 0) {
-    left_c1 = left_c2 = left;
-    left_r1 = 0;
-    left_r2 = rows - 1;
-    for (col = 0; col < left_c1; col++)
-      PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE);
-    for (col = 0; col < left_c2; col++)
-      PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_left(left_r1, left_r2, left_c1, left_c2, cmdline.var);
-  }
-
-  /* Make a ragged right border */
-  if (right >= 0) {
-    right_c1 = right_c2 = cols - right - 1;
-    right_r1 = 0;
-    right_r2 = rows - 1;
-    for (col = right_c1; col < cols; col++)
-      PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE);
-    for (col = right_c2; col < cols; col++)
-      PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_right(right_r1, right_r2, right_c1, right_c2, 
-               cmdline.width, cmdline.var);
-  }
-
-  /* Make a ragged top border */
-  if (top >= 0) {
-    top_r1 = top_r2 = top;
-    top_c1 = 0;
-    top_c2 = cols - 1;
-    for (row = 0; row < top_r1; row++)
-      PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE);
-    for (row = 0; row < top_r2; row++)
-      PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_top(top_c1, top_c2, top_r1, top_r2, cmdline.var);
-  }
-
-  /* Make a ragged bottom border */
-  if (bottom >= 0) {
-    bottom_r1 = bottom_r2 = rows - bottom - 1;
-    bottom_c1 = 0;
-    bottom_c2 = cols - 1;
-    for (row = bottom_r1; row < rows; row++)
-      PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE);
-    for (row = bottom_r2; row < rows; row++)
-      PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_bottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, 
-                cmdline.height, cmdline.var);
-  }
-
-  /* Write pixmap */
-  ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0);
-
-  ppm_freearray(PIX, rows);
-
-  pm_close(stdout);
-  exit(0);
+main(int argc, const char * argv[]) {
+
+    struct CmdlineInfo cmdline;
+    pixel bgcolor, fgcolor;
+    pixval fg_red, fg_green, fg_blue;
+    int rows, cols, row;
+    int left, right, top, bottom;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    cols = cmdline.width;
+    rows = cmdline.height;
+    left = cmdline.left;
+    right = cmdline.right;
+    top = cmdline.top;
+    bottom = cmdline.bottom;
+
+    if (cmdline.bg_rgb)
+        bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL);
+    else
+        PPM_ASSIGN(bgcolor, 0, 0, 0);
+    BG_RED = PPM_GETR(bgcolor);
+    BG_GREEN = PPM_GETG(bgcolor);
+    BG_BLUE = PPM_GETB(bgcolor);
+
+    if (cmdline.fg_rgb)
+        fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL);
+    else
+        PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL);
+    fg_red = PPM_GETR(fgcolor);
+    fg_green = PPM_GETG(fgcolor);
+    fg_blue = PPM_GETB(fgcolor);
+
+    if (cmdline.verbose) {
+        pm_message("width is %d, height is %d, variance is %d.", 
+                   cols, rows, cmdline.var);
+        if (left >= 0)
+            pm_message("ragged left border is required");
+        if (right >= 0)
+            pm_message("ragged right border is required");
+        if (top >= 0)
+            pm_message("ragged top border is required");
+        if (bottom >= 0)
+            pm_message("ragged bottom border is required");
+        pm_message("background is %s",
+                   ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1));
+        pm_message("foreground is %s",
+                   ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1));
+        if (cmdline.randomseedSpec)
+            pm_message("srand() initialized with seed %u", cmdline.randomseed);
+    }
+
+    /* Allocate memory for the whole pixmap */
+    PIX = ppm_allocarray(cols, rows);
+
+    /* First, set all pixel to foreground color */
+    for (row = 0; row < rows; row++) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue);
+    }
+    /* Make a ragged left border */
+    if (left >= 0) {
+        int const left_c1 = left;
+        int const left_c2 = left;
+        int const left_r1 = 0;
+        int const left_r2 = rows - 1;
+
+        unsigned int col;
+
+        for (col = 0; col < left_c1; ++col)
+            PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE);
+        for (col = 0; col < left_c2; ++col)
+            PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE);
+
+        procLeft(left_r1, left_r2, left_c1, left_c2, cmdline.var);
+    }
+
+    /* Make a ragged right border */
+    if (right >= 0) {
+        int const right_c1 = cols - right - 1;
+        int const right_c2 = cols - right - 1;
+        int const right_r1 = 0;
+        int const right_r2 = rows - 1;
+
+        unsigned int col;
+
+        for (col = right_c1; col < cols; col++)
+            PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE);
+        for (col = right_c2; col < cols; col++)
+            PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE);
+
+        procRight(right_r1, right_r2, right_c1, right_c2, 
+                   cmdline.width, cmdline.var);
+    }
+
+    /* Make a ragged top border */
+    if (top >= 0) {
+        int const top_r1 = top;
+        int const top_r2 = top;
+        int const top_c1 = 0;
+        int const top_c2 = cols - 1;
+
+        unsigned int row;
+
+        for (row = 0; row < top_r1; ++row)
+            PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE);
+        for (row = 0; row < top_r2; ++row)
+            PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE);
+
+        procTop(top_c1, top_c2, top_r1, top_r2, cmdline.var);
+    }
+
+    /* Make a ragged bottom border */
+    if (bottom >= 0) {
+        int const bottom_r1 = rows - bottom - 1;
+        int const bottom_r2 = rows - bottom - 1;
+        int const bottom_c1 = 0;
+        int const bottom_c2 = cols - 1;
+
+        unsigned int row;
+
+        for (row = bottom_r1; row < rows; ++row)
+            PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE);
+        for (row = bottom_r2; row < rows; ++row)
+            PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE);
+
+        procBottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, 
+                   cmdline.height, cmdline.var);
+    }
+
+    /* Write pixmap */
+    ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0);
+
+    ppm_freearray(PIX, rows);
+
+    pm_close(stdout);
+
+    return 0;
 }
+
+
+