about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2022-12-31 00:32:37 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2022-12-31 00:32:37 +0000
commita70405848cb06782036364a88e27f452fb0a0b32 (patch)
tree514c1d8e50b2989b77b4e1958ea1e0e149ec13c3
parent1c19660f1cdb62646f324615d4842a46c7681303 (diff)
downloadnetpbm-mirror-a70405848cb06782036364a88e27f452fb0a0b32.tar.gz
netpbm-mirror-a70405848cb06782036364a88e27f452fb0a0b32.tar.xz
netpbm-mirror-a70405848cb06782036364a88e27f452fb0a0b32.zip
promote Development to Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@4489 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_image.c33
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_enc.c1377
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_math.c2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c163
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_util.c71
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c11
-rw-r--r--converter/ppm/ppmtompeg/frame.c68
-rw-r--r--converter/ppm/ppmtompeg/readframe.c103
-rw-r--r--doc/HISTORY18
-rw-r--r--editor/pamcat.c214
-rw-r--r--editor/pamhomography.c760
-rw-r--r--other/pnmcolormap.c135
-rw-r--r--test/Test-Order1
-rwxr-xr-xtest/cut-cat-roundtrip.test18
-rw-r--r--test/ilbm-roundtrip.ok2
-rwxr-xr-xtest/ilbm-roundtrip.test2
-rwxr-xr-xtest/lps-roundtrip.test2
-rw-r--r--test/pamarith.ok2
-rwxr-xr-xtest/pamarith.test2
-rwxr-xr-xtest/pamcat2.test10
-rw-r--r--test/pamcat3.ok19
-rwxr-xr-xtest/pamcat3.test97
-rwxr-xr-xtest/stdin-pam3.test2
-rw-r--r--version.mk4
24 files changed, 1809 insertions, 1307 deletions
diff --git a/converter/other/jpeg2000/libjasper/base/jas_image.c b/converter/other/jpeg2000/libjasper/base/jas_image.c
index 5ee13a0d..c6f984c1 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_image.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_image.c
@@ -675,7 +675,7 @@ int jas_image_fmtfromname(char *name)
 		return -1;
 	}
 	++ext;
-	/* Try to find a format that uses this extension. */	
+	/* Try to find a format that uses this extension. */
 	for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i,
 	  ++fmtinfo) {
 		/* Do we have a match? */
@@ -686,25 +686,28 @@ int jas_image_fmtfromname(char *name)
 	return -1;
 }
 
-/******************************************************************************\
+/*****************************************************************************\
 * Miscellaneous operations.
-\******************************************************************************/
+\*****************************************************************************/
 
-uint_fast32_t jas_image_rawsize(jas_image_t *image)
-{
-	uint_fast32_t rawsize;
-	uint_fast32_t cmptno;
-	jas_image_cmpt_t *cmpt;
+uint_fast32_t
+jas_image_rawsize(jas_image_t * const imageP) {
+/*----------------------------------------------------------------------------
+   The raw size of the image, i.e. the number of bytes the raster of the image
+   would take if just represented simply, with no compression.
+-----------------------------------------------------------------------------*/
+    uint_fast32_t rawsize;
+    uint_fast32_t cmptno;
 
-	rawsize = 0;
-	for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) {
-		cmpt = image->cmpts_[cmptno];
-		rawsize += (cmpt->width_ * cmpt->height_ * cmpt->prec_ +
-		  7) / 8;
-	}
-	return rawsize;
+    for (cmptno = 0, rawsize = 0; cmptno < imageP->numcmpts_; ++cmptno) {
+        jas_image_cmpt_t * const cmptP = imageP->cmpts_[cmptno];
+        rawsize += (cmptP->width_ * cmptP->height_ * cmptP->prec_ + 7) / 8;
+    }
+    return rawsize;
 }
 
+
+
 void jas_image_delcmpt(jas_image_t *image, uint_fast16_t cmptno)
 {
 	if (cmptno >= image->numcmpts_) {
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
index 9db41ca2..0c77a94e 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
@@ -69,7 +69,6 @@ static void prc_destroy(jpc_enc_prc_t *prcs);
 static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp,
   jpc_enc_prc_t *prc);
 static void cblk_destroy(jpc_enc_cblk_t *cblks);
-int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size);
 static void pass_destroy(jpc_enc_pass_t *pass);
 void jpc_enc_dump(jpc_enc_t *enc);
 
@@ -78,17 +77,8 @@ void jpc_enc_dump(jpc_enc_t *enc);
 \*****************************************************************************/
 
 void quantize(jas_matrix_t *data, jpc_fix_t stepsize);
-static int jpc_enc_encodemainhdr(jpc_enc_t *enc);
-static int jpc_enc_encodemainbody(jpc_enc_t *enc);
-int jpc_enc_encodetiledata(jpc_enc_t *enc);
 jpc_enc_t *jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image);
 void jpc_enc_destroy(jpc_enc_t *enc);
-static int jpc_enc_encodemainhdr(jpc_enc_t *enc);
-static int jpc_enc_encodemainbody(jpc_enc_t *enc);
-int jpc_enc_encodetiledata(jpc_enc_t *enc);
-int setins(int numvalues, jpc_flt_t *values, jpc_flt_t value);
-static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image);
-void jpc_enc_cp_destroy(jpc_enc_cp_t *cp);
 
 
 
@@ -143,7 +133,6 @@ typedef enum {
     OPT_NUMGBITS,
     OPT_RATE,
     OPT_ILYRRATES,
-    OPT_JP2OVERHEAD
 } optid_t;
 
 jas_taginfo_t encopts[] = {
@@ -173,7 +162,6 @@ jas_taginfo_t encopts[] = {
     {OPT_NUMGBITS, "numgbits"},
     {OPT_RATE, "rate"},
     {OPT_ILYRRATES, "ilyrrates"},
-    {OPT_JP2OVERHEAD, "_jp2overhead"},
     {-1, 0}
 };
 
@@ -230,72 +218,46 @@ trace(const char * const fmt, ...) {
 
 
 
-int
-jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr) {
-
-    jpc_enc_t *enc;
-    jpc_enc_cp_t *cp;
-
-    enc = 0;
-    cp = 0;
-
-    jpc_initluts();
-
-    if (!(cp = cp_create(optstr, image))) {
-        fprintf(stderr, "invalid JP encoder options\n");
-        goto error;
-    }
-
-    if (!(enc = jpc_enc_create(cp, out, image))) {
-        goto error;
-    }
-    cp = 0;
+/*****************************************************************************\
+* Option parsing code.
+\*****************************************************************************/
 
-    /* Encode the main header. */
-    if (jpc_enc_encodemainhdr(enc)) {
-        goto error;
-    }
+static void
+ratestrtosize(const char *    const s,
+              uint_fast32_t   const rawsize,
+              uint_fast32_t * const sizeP) {
 
-    /* Encode the main body.  This constitutes most of the encoding work. */
-    if (jpc_enc_encodemainbody(enc)) {
-        goto error;
-    }
+    if (strchr(s, 'B')) {
+        *sizeP = atoi(s);
+    } else {
+        jpc_flt_t const f = atof(s);
 
-    /* Write EOC marker segment. */
-    if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) {
-        goto error;
-    }
-    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-        fprintf(stderr, "cannot write EOI marker\n");
-        goto error;
+        if (f < 0) {
+            *sizeP = 0;
+        } else if (f > 1.0) {
+            *sizeP = rawsize + 1;
+        } else {
+            *sizeP = f * rawsize;
+        }
     }
-    jpc_ms_destroy(enc->mrk);
-    enc->mrk = 0;
+}
 
-    if (jas_stream_flush(enc->out)) {
-        goto error;
-    }
 
-    jpc_enc_destroy(enc);
 
-    return 0;
+static void
+cp_destroy(jpc_enc_cp_t *cp) {
 
-error:
-    if (cp) {
-        jpc_enc_cp_destroy(cp);
-    }
-    if (enc) {
-        jpc_enc_destroy(enc);
+    if (cp->ccps) {
+        if (cp->tcp.ilyrrates) {
+            jas_free(cp->tcp.ilyrrates);
+        }
+        jas_free(cp->ccps);
     }
-    return -1;
+    jas_free(cp);
 }
 
 
 
-/*****************************************************************************\
-* Option parsing code.
-\*****************************************************************************/
-
 static jpc_enc_cp_t *
 cp_create(char *optstr, jas_image_t *image) {
 
@@ -314,7 +276,6 @@ cp_create(char *optstr, jas_image_t *image) {
     uint_fast16_t prcwidthexpn;
     uint_fast16_t prcheightexpn;
     bool enablemct;
-    uint_fast32_t jp2overhead;
     uint_fast16_t lyrno;
     uint_fast32_t hsteplcm;
     uint_fast32_t vsteplcm;
@@ -332,7 +293,6 @@ cp_create(char *optstr, jas_image_t *image) {
     prcwidthexpn = 15;
     prcheightexpn = 15;
     enablemct = true;
-    jp2overhead = 0;
 
     cp->ccps = 0;
     cp->debug = 0;
@@ -380,6 +340,10 @@ cp_create(char *optstr, jas_image_t *image) {
 
     cp->rawsize = jas_image_rawsize(image);
     cp->totalsize = UINT_FAST32_MAX;
+        /* Set default value, the special value that means size is unlimited
+           (so lossless coding is called for).  To be overridden if user
+           specified
+        */
 
     tcp = &cp->tcp;
     tcp->csty = 0;
@@ -492,12 +456,8 @@ cp_create(char *optstr, jas_image_t *image) {
             cp->tccp.numgbits = atoi(jas_tvparser_getval(tvp));
             break;
         case OPT_RATE:
-            if (ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize,
-              &cp->totalsize)) {
-                fprintf(stderr,
-                  "ignoring bad rate specifier %s\n",
-                  jas_tvparser_getval(tvp));
-            }
+            ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize,
+                          &cp->totalsize);
             break;
         case OPT_ILYRRATES:
             if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates,
@@ -505,13 +465,10 @@ cp_create(char *optstr, jas_image_t *image) {
                 fprintf(stderr,
                         "warning: invalid intermediate layer rates specifier "
                         "ignored (%s)\n",
-                  jas_tvparser_getval(tvp));
+                        jas_tvparser_getval(tvp));
             }
             break;
 
-        case OPT_JP2OVERHEAD:
-            jp2overhead = atoi(jas_tvparser_getval(tvp));
-            break;
         default:
             fprintf(stderr, "warning: ignoring invalid option %s\n",
              jas_tvparser_gettag(tvp));
@@ -522,11 +479,6 @@ cp_create(char *optstr, jas_image_t *image) {
     jas_tvparser_destroy(tvp);
     tvp = 0;
 
-    if (cp->totalsize != UINT_FAST32_MAX) {
-        cp->totalsize = (cp->totalsize > jp2overhead) ?
-          (cp->totalsize - jp2overhead) : 0;
-    }
-
     if (cp->imgareatlx == UINT_FAST32_MAX) {
         cp->imgareatlx = 0;
     } else {
@@ -697,20 +649,28 @@ cp_create(char *optstr, jas_image_t *image) {
         /* The intermediate layers rates must increase monotonically. */
         for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) {
             if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) {
-                fprintf(stderr,
-                        "intermediate layer rates must increase "
-                        "monotonically\n");
+                pm_message("Compression rate for Layer %u (%f) "
+                           "is not greater than that for Layer %u (%f).  "
+                           "Rates must increase at every layer",
+                           (unsigned)(lyrno+1),
+                           jpc_fixtodbl(tcp->ilyrrates[lyrno + 1]),
+                           (unsigned)lyrno,
+                           jpc_fixtodbl(tcp->ilyrrates[lyrno]));
                 goto error;
             }
         }
         /* The intermediate layer rates must be less than the overall rate. */
         if (cp->totalsize != UINT_FAST32_MAX) {
             for (lyrno = 0; lyrno < tcp->numlyrs - 1; ++lyrno) {
-                if (jpc_fixtodbl(tcp->ilyrrates[lyrno]) >
-                    ((double) cp->totalsize) / cp->rawsize) {
-                    fprintf(stderr,
-                            "warning: intermediate layer rates must be "
-                            "less than overall rate\n");
+                double const thisLyrRate = jpc_fixtodbl(tcp->ilyrrates[lyrno]);
+                double const completeRate =
+                    ((double) cp->totalsize) / cp->rawsize;
+                if (thisLyrRate > completeRate) {
+                    pm_message(
+                        "Compression rate for Layer %u is %f, "
+                        "which is greater than the rate for the complete "
+                        "image (%f)",
+                        (unsigned)lyrno, thisLyrRate, completeRate);
                     goto error;
                 }
             }
@@ -732,118 +692,17 @@ error:
         jas_tvparser_destroy(tvp);
     }
     if (cp) {
-        jpc_enc_cp_destroy(cp);
-    }
-    return 0;
-}
-
-
-
-void
-jpc_enc_cp_destroy(jpc_enc_cp_t *cp) {
-
-    if (cp->ccps) {
-        if (cp->tcp.ilyrrates) {
-            jas_free(cp->tcp.ilyrrates);
-        }
-        jas_free(cp->ccps);
-    }
-    jas_free(cp);
-}
-
-
-
-int
-ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size) {
-
-    char *cp;
-    jpc_flt_t f;
-
-    /* Note: This function must not modify output size on failure. */
-    if ((cp = strchr(s, 'B'))) {
-        *size = atoi(s);
-    } else {
-        f = atof(s);
-        if (f < 0) {
-            *size = 0;
-        } else if (f > 1.0) {
-            *size = rawsize + 1;
-        } else {
-            *size = f * rawsize;
-        }
-    }
-    return 0;
-}
-
-/*****************************************************************************\
-* Encoder constructor and destructor.
-\*****************************************************************************/
-
-jpc_enc_t *
-jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image) {
-
-    jpc_enc_t *enc;
-
-    enc = 0;
-
-    if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) {
-        goto error;
-    }
-
-    enc->image = image;
-    enc->out = out;
-    enc->cp = cp;
-    enc->cstate = 0;
-    enc->tmpstream = 0;
-    enc->mrk = 0;
-    enc->curtile = 0;
-
-    if (!(enc->cstate = jpc_cstate_create())) {
-        goto error;
-    }
-    enc->len = 0;
-    enc->mainbodysize = 0;
-
-    return enc;
-
-error:
-
-    if (enc) {
-        jpc_enc_destroy(enc);
+        cp_destroy(cp);
     }
     return 0;
 }
 
 
 
-void
-jpc_enc_destroy(jpc_enc_t *enc) {
-
-    /* The image object (i.e., enc->image) and output stream object
-       (i.e., enc->out) are created outside of the encoder.
-       Therefore, they must not be destroyed here.
-    */
-
-    if (enc->curtile) {
-        jpc_enc_tile_destroy(enc->curtile);
-    }
-    if (enc->cp) {
-        jpc_enc_cp_destroy(enc->cp);
-    }
-    if (enc->cstate) {
-        jpc_cstate_destroy(enc->cstate);
-    }
-    if (enc->tmpstream) {
-        jas_stream_close(enc->tmpstream);
-    }
-
-    jas_free(enc);
-}
-
-
-
 static int
-jpc_enc_encodemainhdr(jpc_enc_t *enc) {
+encodemainhdr(jpc_enc_t *enc) {
+
+    uint_fast32_t const maintlrlen = 2;
 
     jpc_siz_t *siz;
     jpc_cod_t *cod;
@@ -961,7 +820,7 @@ jpc_enc_encodemainhdr(jpc_enc_t *enc) {
                   (analgain + 1)), bandinfo->synenergywt);
             } else {
                 absstepsize = jpc_inttofix(1);
-            }   
+            }
             cp->ccps[cmptno].stepsizes[bandno] =
               jpc_abstorelstepsize(absstepsize,
               cp->ccps[cmptno].prec + analgain);
@@ -1038,61 +897,32 @@ jpc_enc_encodemainhdr(jpc_enc_t *enc) {
         enc->mrk = 0;
     }
 
-#define MAINTLRLEN  2
     mainhdrlen = jas_stream_getrwcount(enc->out) - startoff;
     enc->len += mainhdrlen;
     if (enc->cp->totalsize != UINT_FAST32_MAX) {
-        uint_fast32_t overhead;
-        overhead = mainhdrlen + MAINTLRLEN;
-        enc->mainbodysize = (enc->cp->totalsize >= overhead) ?
-          (enc->cp->totalsize - overhead) : 0;
-    } else {
-        enc->mainbodysize = UINT_FAST32_MAX;
-    }
-
-    return 0;
-}
-
-
-
-int
-jpc_enc_encodetiledata(jpc_enc_t *enc) {
-
-    assert(enc->tmpstream);
-    if (jpc_enc_encpkts(enc, enc->tmpstream)) {
-        return -1;
-    }
-    return 0;
-}
-
-
-
-void
-quantize(jas_matrix_t *data, jpc_fix_t stepsize) {
+        uint_fast32_t const overhead = mainhdrlen + maintlrlen;
 
-    int i;
-    int j;
-    jpc_fix_t t;
-
-    if (stepsize == jpc_inttofix(1)) {
-        return;
-    }
-
-    for (i = 0; i < jas_matrix_numrows(data); ++i) {
-        for (j = 0; j < jas_matrix_numcols(data); ++j) {
-            t = jas_matrix_get(data, i, j);
+        if (overhead > enc->cp->totalsize) {
+            pm_message("Requested limit on image size of %u bytes "
+                       "is not possible because it is less than "
+                       "the image metadata size (%u bytes)",
+                       (unsigned)enc->cp->totalsize, (unsigned)overhead);
+            return -1;
+        }
+        enc->mainbodysize = enc->cp->totalsize - overhead;
+        /* This has never actually worked.  'totalsize' is supposed to be
+           the total all-in, so if you request total size 200, you should
+           get an output file 200 bytes or smaller; but we see 209 bytes.
+           Furthermore, at 194 bytes, we get a warning that an empty layer
+           is generated, which probably is actually an error.
 
-{
-    if (t < 0) {
-        t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize));
+           We should fix this some day.
+        */
     } else {
-        t = jpc_fix_div(t, stepsize);
+        enc->mainbodysize = UINT_FAST32_MAX;
     }
-}
 
-            jas_matrix_set(data, i, j, t);
-        }
-    }
+    return 0;
 }
 
 
@@ -1166,12 +996,12 @@ calcrdslopes(jpc_enc_cblk_t *cblk) {
 
 static void
 traceLayerSizes(const uint_fast32_t * const lyrSizes,
-                unsigned int          const layerCt) {
+                uint_fast32_t         const layerCt) {
 
     if (jas_getdbglevel() > 0) {
-        unsigned int i;
+        uint_fast32_t i;
         for (i = 0; i < layerCt; ++i) {
-            fprintf(stderr, "Layer %u size = ", i);
+            fprintf(stderr, "Layer %u size = ", (unsigned)i);
 
             if (lyrSizes[i] == UINT_FAST32_MAX)
                 fprintf(stderr, "Unlimited");
@@ -1189,48 +1019,28 @@ computeLayerSizes(jpc_enc_t *      const encP,
                   jpc_enc_tile_t * const tileP,
                   jpc_enc_cp_t *   const cpP,
                   double           const rho,
-                  long             const tilehdrlen,
-                  const char **    const errorP) {
+                  long             const tilehdrlen) {
 
     /* Note that in allowed sizes, UINT_FAST32_MAX is a special value meaning
        "unlimited".
     */
 
-    unsigned int const lastLyrno = tileP->numlyrs - 1;
+    uint_fast32_t const lastLyrno = tileP->numlyrs - 1;
 
-    unsigned int lyrno;
+    uint_fast32_t lyrno;
 
     assert(tileP->numlyrs > 0);
 
     for (lyrno = 0; lyrno < lastLyrno; ++lyrno) {
-        tileP->lyrsizes[lyrno] = tileP->rawsize * jpc_fixtodbl(
-            cpP->tcp.ilyrrates[lyrno]);
+        tileP->lyrsizes[lyrno] =
+            MAX(tileP->rawsize *
+                jpc_fixtodbl(cpP->tcp.ilyrrates[lyrno]),
+                tilehdrlen + 1) - tilehdrlen;
     }
 
     tileP->lyrsizes[lastLyrno] =
-        (cpP->totalsize != UINT_FAST32_MAX) ?
-        (rho * encP->mainbodysize) : UINT_FAST32_MAX;
-
-
-    /* Subtract 'tilehdrlen' from every layer. */
-
-    for (lyrno = 0; lyrno < tileP->numlyrs; ++lyrno) {
-        if (tileP->lyrsizes[lyrno] != UINT_FAST32_MAX) {
-            if (tilehdrlen <= tileP->lyrsizes[lyrno]) {
-                tileP->lyrsizes[lyrno] -= tilehdrlen;
-            } else {
-                tileP->lyrsizes[lyrno] = 0;
-            }
-        }
-    }
-
-    if (tileP->lyrsizes[lastLyrno] < 1)
-        pm_asprintf(errorP, "Cannot make image that small (%u bytes).  "
-                    "Even with pixels compressed as far as possible, metadata "
-                    "would exceed the limit",
-                    (unsigned)cpP->totalsize);
-    else
-        *errorP = NULL;
+        (cpP->totalsize == UINT_FAST32_MAX) ?
+        UINT_FAST32_MAX : (rho * encP->mainbodysize);
 
     traceLayerSizes(tileP->lyrsizes, tileP->numlyrs);
 }
@@ -1313,8 +1123,8 @@ trace_layeringinfo(jpc_enc_t * const encP) {
 
 static void
 validateCumlensIncreases(const uint_fast32_t * const cumlens,
-                         unsigned int          const numlyrs) {
-    unsigned int lyrno;
+                         uint_fast32_t          const numlyrs) {
+    uint_fast32_t lyrno;
 
     for (lyrno = 1; lyrno < numlyrs - 1; ++lyrno) {
         if (cumlens[lyrno - 1] > cumlens[lyrno]) {
@@ -1404,7 +1214,7 @@ findMinMaxRDSlopeValues(jpc_enc_tile_t * const tileP,
 static void
 performTier2CodingOneLayer(jpc_enc_t *      const encP,
                            jpc_enc_tile_t * const tileP,
-                           unsigned int     const lyrno,
+                           uint_fast32_t     const lyrno,
                            jas_stream_t *   const outP,
                            const char **    const errorP) {
 /*----------------------------------------------------------------------------
@@ -1444,9 +1254,9 @@ performTier2CodingOneLayer(jpc_enc_t *      const encP,
 static void
 assignHighSlopePassesToLayer(jpc_enc_t *      const encP,
                              jpc_enc_tile_t * const tileP,
-                             unsigned int     const lyrno,
-                             bool             const haveThresh,
-                             jpc_flt_t        const thresh) {
+                             uint_fast32_t     const lyrno,
+                             bool              const haveThresh,
+                             jpc_flt_t         const thresh) {
 /*----------------------------------------------------------------------------
   Assign all passes with R-D slopes greater than or equal to 'thresh' to layer
   'lyrno' and the rest to no layer.
@@ -1505,8 +1315,7 @@ assignHighSlopePassesToLayer(jpc_enc_t *      const encP,
                                         for (; passP != endpassesP; ++passP) {
                                             passP->lyrno = -1;
                                         }
-                                    
-                                    }   
+                                    }
                                 }
                             }
                         }
@@ -1522,7 +1331,7 @@ assignHighSlopePassesToLayer(jpc_enc_t *      const encP,
 static void
 doLayer(jpc_enc_t *      const encP,
         jpc_enc_tile_t * const tileP,
-        unsigned int     const lyrno,
+        uint_fast32_t    const lyrno,
         uint_fast32_t    const allowedSize,
         jpc_flt_t        const mnrdslope,
         jpc_flt_t        const mxrdslope,
@@ -1550,7 +1359,7 @@ doLayer(jpc_enc_t *      const encP,
         long pos;
         jpc_flt_t lo;
         jpc_flt_t hi;
-        unsigned int numiters;
+        uint_fast32_t numiters;
 
         lo = mnrdslope;  /* initial value */
         hi = mxrdslope;  /* initial value */
@@ -1559,52 +1368,46 @@ doLayer(jpc_enc_t *      const encP,
         goodThresh = 0;     /* initial value */
 
         do {
-            if (allowedSize == UINT_FAST32_MAX) {
-                /* There's no rate constraint (This can be true of the last
-                   layer, e.g. for lossless coding). */
-                goodThresh = -1;
-                haveGoodThresh = true;
-            } else {
-                jpc_flt_t const thresh = (lo + hi) / 2;
-
-                int rc;
-                long oldpos;
-
-                /* Save the tier 2 coding state. */
-                jpc_save_t2state(encP);
-                oldpos = jas_stream_tell(outP);
-                assert(oldpos >= 0);
-
-                assignHighSlopePassesToLayer(encP, tileP, lyrno, true, thresh);
-
-                performTier2CodingOneLayer(encP, tileP, lyrno, outP, errorP);
-
-                if (!*errorP) {
-                    pos = jas_stream_tell(outP);
-
-                    /* Check the rate constraint. */
-                    assert(pos >= 0);
-                    if (pos > allowedSize) {
-                        /* The rate is too high. */
-                        lo = thresh;
-                    } else if (pos <= allowedSize) {
-                        /* The rate is low enough, so try higher. */
-                        hi = thresh;
-                        if (!haveGoodThresh || thresh < goodThresh) {
-                            goodThresh = thresh;
-                            haveGoodThresh = true;
-                        }
+            jpc_flt_t const thresh = (lo + hi) / 2;
+
+            int rc;
+            long oldpos;
+
+            /* Save the tier 2 coding state. */
+            jpc_save_t2state(encP);
+            oldpos = jas_stream_tell(outP);
+            assert(oldpos >= 0);
+
+            assignHighSlopePassesToLayer(encP, tileP, lyrno, true, thresh);
+
+            performTier2CodingOneLayer(encP, tileP, lyrno, outP, errorP);
+
+            if (!*errorP) {
+                pos = jas_stream_tell(outP);
+
+                /* Check the rate constraint. */
+                assert(pos >= 0);
+                if (pos > allowedSize) {
+                    /* The rate is too high. */
+                    lo = thresh;
+                } else if (pos <= allowedSize) {
+                    /* The rate is low enough, so try higher. */
+                    hi = thresh;
+                    if (!haveGoodThresh || thresh < goodThresh) {
+                        goodThresh = thresh;
+                        haveGoodThresh = true;
                     }
                 }
-                /* Restore the tier 2 coding state. */
-                jpc_restore_t2state(encP);
-                rc = jas_stream_seek(outP, oldpos, SEEK_SET);
-                if (rc < 0)
-                    abort();
-
-                trace("iter %u: allowedlen=%08ld actuallen=%08ld thresh=%f",
-                      numiters, allowedSize, pos, thresh);
             }
+            /* Restore the tier 2 coding state. */
+            jpc_restore_t2state(encP);
+            rc = jas_stream_seek(outP, oldpos, SEEK_SET);
+            if (rc < 0)
+                abort();
+
+            trace("iter %u: allowedlen=%08ld actuallen=%08ld thresh=%f",
+                  numiters, allowedSize, pos, thresh);
+
             ++numiters;
         } while (lo < hi - 1e-3 && numiters < 32 && !*errorP);
     }
@@ -1625,10 +1428,10 @@ doLayer(jpc_enc_t *      const encP,
 
 
 static void
-performTier2Coding(jpc_enc_t *     const encP,
-                   unsigned int    const numlyrs,
-                   uint_fast32_t * const cumlens,
-                   const char **   const errorP) {
+performTier2Coding(jpc_enc_t *      const encP,
+                   uint_fast32_t    const numlyrs,
+                   uint_fast32_t *  const cumlens,
+                   const char **    const errorP) {
 /*----------------------------------------------------------------------------
    Encode in 'numlyrs' layers, such that at each layer L, the size is
    cumlens[L].
@@ -1636,7 +1439,7 @@ performTier2Coding(jpc_enc_t *     const encP,
     jpc_enc_tile_t * const tileP = encP->curtile;
 
     jas_stream_t * outP;
-    unsigned int lyrno;
+    uint_fast32_t lyrno;
     jpc_flt_t mnrdslope;
     jpc_flt_t mxrdslope;
 
@@ -1669,6 +1472,562 @@ performTier2Coding(jpc_enc_t *     const encP,
 
 
 
+
+
+static void
+encodeTileBody(jpc_enc_t *   const encoderP,
+               long          const tilehdrlen,
+               const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Encode the body of encoder *encoderP's current tile, writing the encoded
+   result to the encoder's output stream.
+
+   Assume the tile header is already in that stream, and its length is
+   'tilehdrlen'.
+-----------------------------------------------------------------------------*/
+    jpc_enc_tile_t * const tileP = encoderP->curtile;
+    jpc_enc_cp_t *   const cp    = encoderP->cp;
+
+    int rc;
+
+    rc = jpc_enc_enccblks(encoderP);
+    if (rc != 0)
+        pm_asprintf(errorP, "jpc_enc_enccblks() failed");
+    else {
+        double const rho =
+            (double) (tileP->brx - tileP->tlx) * (tileP->bry - tileP->tly) /
+            ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight -
+                                                   cp->imgareatly));
+        const char * error;
+
+        tileP->rawsize = cp->rawsize * rho;
+
+        computeLayerSizes(encoderP, tileP, cp, rho, tilehdrlen);
+
+        performTier2Coding(encoderP, tileP->numlyrs, tileP->lyrsizes, &error);
+
+        if (error) {
+            pm_asprintf(errorP, "Tier 2 coding failed.  %s", error);
+            pm_strfree(error);
+        } else {
+            int rc;
+
+            rc = jpc_enc_encpkts(encoderP, encoderP->tmpstream);
+            if (rc != 0)
+                pm_asprintf(errorP, "jpc_enc_encpkts() failed\n");
+            else
+                *errorP = NULL;
+        }
+    }
+}
+
+
+
+static void
+quantizeBand(jpc_enc_band_t * const bandP,
+             jpc_enc_tile_t * const tileP,
+             jpc_enc_cp_t *   const cp,
+             int              const prec,
+             int              const tccp_numgbits,
+             int *            const numgbitsP) {
+
+    if (bandP->data) {
+        int actualnumbps;
+        uint_fast32_t y;
+        jpc_fix_t mxmag;
+
+        for (y = 0, actualnumbps = 0, mxmag = 0;
+             y < jas_matrix_numrows(bandP->data);
+             ++y) {
+            uint_fast32_t x;
+
+            for (x = 0; x < jas_matrix_numcols(bandP->data); ++x)
+                mxmag = MAX(mxmag, abs(jas_matrix_get(bandP->data, y, x)));
+        }
+        if (tileP->intmode)
+            actualnumbps = jpc_firstone(mxmag) + 1;
+        else
+            actualnumbps = jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS;
+
+        *numgbitsP = actualnumbps - (prec - 1 + bandP->analgain);
+
+        if (!tileP->intmode) {
+            bandP->absstepsize =
+                jpc_fix_div(
+                    jpc_inttofix(1 << (bandP->analgain + 1)),
+                    bandP->synweight);
+        } else {
+            bandP->absstepsize = jpc_inttofix(1);
+        }
+        bandP->stepsize = jpc_abstorelstepsize(
+            bandP->absstepsize, prec + bandP->analgain);
+        /* I couldn't figure out what the calculation with tccp_numgbits and
+           stepsize does (or even what a step size is), but there is an
+           assertion elsewhere than the number here is at least at large as
+           the 'numbps' value for every code block, which means
+           'actualnumbps'.  In practice, we saw that not be true, so we added
+           the code to make 'actualnumbps' the floor here in hopes that would
+           fix the problem.  But with the change, the image that caused the
+           assertion failure produces incorrect output.  22.11.07
+        */
+        bandP->numbps =
+            MAX(actualnumbps,
+                tccp_numgbits + JPC_QCX_GETEXPN(bandP->stepsize) - 1);
+
+        if (!tileP->intmode && bandP->data)
+            quantize(bandP->data, bandP->absstepsize);
+    } else
+        *numgbitsP = 0;
+}
+
+
+
+static int
+encodemainbody(jpc_enc_t *enc) {
+
+    int tileno;
+    int i;
+    jpc_sot_t *sot;
+    int rlvlno;
+    jpc_qcc_t *qcc;
+    jpc_cod_t *cod;
+    int adjust;
+    int j;
+    int absbandno;
+    long tilehdrlen;
+    long tilelen;
+    jpc_enc_tile_t *tile;
+    jpc_enc_cp_t *cp;
+    int samestepsizes;
+    jpc_enc_ccp_t *ccps;
+    jpc_enc_tccp_t *tccp;
+    int mingbits; /* Minimum number of guard bits needed */
+    const char * error;
+
+    cp = enc->cp;
+
+    for (tileno = 0; tileno < cp->numtiles; ++tileno) {
+        uint_fast16_t cmptno;
+
+        enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno);
+        if (!enc->curtile)
+            abort();
+
+        tile = enc->curtile;
+
+        if (jas_getdbglevel() >= 10) {
+            jpc_enc_dump(enc);
+        }
+
+        for (cmptno = 0; cmptno < tile->numtcmpts; ++cmptno) {
+
+            jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno];
+
+            if (!cp->ccps[cmptno].sgnd) {
+                adjust = 1 << (cp->ccps[cmptno].prec - 1);
+                for (i = 0; i < jas_matrix_numrows(comp->data); ++i) {
+                    for (j = 0; j < jas_matrix_numcols(comp->data); ++j) {
+                        *jas_matrix_getref(comp->data, i, j) -= adjust;
+                    }
+                }
+            }
+        }
+
+        if (!tile->intmode) {
+            uint_fast16_t cmptno;
+            for (cmptno = 0; cmptno < tile->numtcmpts; ++cmptno) {
+                jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno];
+                jas_matrix_asl(comp->data, JPC_FIX_FRACBITS);
+            }
+        }
+
+        switch (tile->mctid) {
+        case JPC_MCT_RCT:
+            assert(jas_image_numcmpts(enc->image) == 3);
+            jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data,
+                    tile->tcmpts[2].data);
+            break;
+        case JPC_MCT_ICT:
+            assert(jas_image_numcmpts(enc->image) == 3);
+            jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data,
+                    tile->tcmpts[2].data);
+            break;
+        default:
+            break;
+        }
+
+        for (i = 0; i < jas_image_numcmpts(enc->image); ++i) {
+            jpc_enc_tcmpt_t * const comp = &tile->tcmpts[i];
+            jpc_tsfb_analyze(comp->tsfb,
+                             ((comp->qmfbid == JPC_COX_RFT) ?
+                              JPC_TSFB_RITIMODE : 0), comp->data);
+
+        }
+
+        for (cmptno = 0; cmptno < tile->numtcmpts; ++cmptno) {
+
+            jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno];
+
+            mingbits = 0;
+            absbandno = 0;
+            /* All bands must have a corresponding quantizer step size,
+               even if they contain no samples and are never coded. */
+            /* Some bands may not be hit by the loop below, so we must
+               initialize all of the step sizes to a sane value. */
+            memset(comp->stepsizes, 0, sizeof(comp->stepsizes));
+            for (rlvlno = 0; rlvlno < comp->numrlvls; ++rlvlno) {
+                jpc_enc_rlvl_t * const lvl = &comp->rlvls[rlvlno];
+
+                unsigned int bandno;
+
+                if (!lvl->bands) {
+                    absbandno += rlvlno ? 3 : 1;
+                    continue;
+                }
+                for (bandno = 0; bandno < lvl->numbands; ++bandno) {
+                    jpc_enc_band_t * const band = &lvl->bands[bandno];
+
+                    int numgbits;
+
+                    quantizeBand(band, tile, cp,
+                                 cp->ccps[cmptno].prec,
+                                 cp->tccp.numgbits,
+                                 &numgbits);
+
+                    mingbits = MAX(mingbits, numgbits);
+
+                    comp->stepsizes[absbandno] = band->stepsize;
+
+                    ++absbandno;
+                }
+            }
+
+            assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS);
+            if (!tile->intmode) {
+                jas_matrix_divpow2(comp->data,
+                                   JPC_FIX_FRACBITS - JPC_NUMEXTRABITS);
+            } else {
+                jas_matrix_asl(comp->data, JPC_NUMEXTRABITS);
+            }
+        }
+
+        if (mingbits > cp->tccp.numgbits) {
+            fprintf(stderr, "error: too few guard bits (need at least %d)\n",
+                    mingbits);
+            return -1;
+        }
+
+        enc->tmpstream = jas_stream_memopen(0, 0);
+        if (!enc->tmpstream) {
+            fprintf(stderr, "cannot open tmp file\n");
+            return -1;
+        }
+
+        /* Write the tile header. */
+        enc->mrk = jpc_ms_create(JPC_MS_SOT);
+        if (!enc->mrk)
+            return -1;
+        sot = &enc->mrk->parms.sot;
+        sot->len = 0;
+        sot->tileno = tileno;
+        sot->partno = 0;
+        sot->numparts = 1;
+        if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+            fprintf(stderr, "cannot write SOT marker\n");
+            return -1;
+        }
+        jpc_ms_destroy(enc->mrk);
+        enc->mrk = 0;
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+        tccp = &cp->tccp;
+        for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
+            jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno];
+            jpc_enc_tcmpt_t * const comp0 = &tile->tcmpts[0];
+
+            if (comp->numrlvls != tccp->maxrlvls) {
+                if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
+                    return -1;
+                }
+                /* XXX = this is not really correct. we are using comp #0's
+                   precint sizes and other characteristics */
+                cod = &enc->mrk->parms.cod;
+                cod->compparms.csty = 0;
+                cod->compparms.numdlvls = comp0->numrlvls - 1;
+                cod->prg = tile->prg;
+                cod->numlyrs = tile->numlyrs;
+                cod->compparms.cblkwidthval =
+                    JPC_COX_CBLKSIZEEXPN(comp0->cblkwidthexpn);
+                cod->compparms.cblkheightval =
+                    JPC_COX_CBLKSIZEEXPN(comp0->cblkheightexpn);
+                cod->compparms.cblksty = comp0->cblksty;
+                cod->compparms.qmfbid = comp0->qmfbid;
+                cod->mctrans = (tile->mctid != JPC_MCT_NONE);
+                for (i = 0; i < comp0->numrlvls; ++i) {
+                    cod->compparms.rlvls[i].parwidthval =
+                        comp0->rlvls[i].prcwidthexpn;
+                    cod->compparms.rlvls[i].parheightval =
+                        comp0->rlvls[i].prcheightexpn;
+                }
+                if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+                    return -1;
+                }
+                jpc_ms_destroy(enc->mrk);
+                enc->mrk = 0;
+            }
+        }
+
+        for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
+            jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno];
+
+            ccps = &cp->ccps[cmptno];
+            if (ccps->numstepsizes == comp->numstepsizes) {
+                unsigned int bandno;
+                samestepsizes = 1;
+                for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) {
+                    if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) {
+                        samestepsizes = 0;
+                        break;
+                    }
+                }
+            } else {
+                samestepsizes = 0;
+            }
+            if (!samestepsizes) {
+                if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
+                    return -1;
+                }
+                qcc = &enc->mrk->parms.qcc;
+                qcc->compno = cmptno;
+                qcc->compparms.numguard = cp->tccp.numgbits;
+                qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ?
+                    JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+                qcc->compparms.numstepsizes = comp->numstepsizes;
+                qcc->compparms.stepsizes = comp->stepsizes;
+                if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+                    return -1;
+                }
+                qcc->compparms.stepsizes = 0;
+                jpc_ms_destroy(enc->mrk);
+                enc->mrk = 0;
+            }
+        }
+
+        /* Write a SOD marker to indicate the end of the tile header. */
+        if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) {
+            return -1;
+        }
+        if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+            fprintf(stderr, "cannot write SOD marker\n");
+            return -1;
+        }
+        jpc_ms_destroy(enc->mrk);
+        enc->mrk = 0;
+        tilehdrlen = jas_stream_getrwcount(enc->tmpstream);
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+        encodeTileBody(enc, tilehdrlen, &error);
+            /* Encodes current tile; writes to output file */
+
+        if (error) {
+            fprintf(stderr, "Failed to encode body of tile %u.  %s\n",
+                    tileno, error);
+            pm_strfree(error);
+            return -1;
+        }
+        tilelen = jas_stream_tell(enc->tmpstream);
+
+        if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) {
+            return -1;
+        }
+        jpc_putuint32(enc->tmpstream, tilelen);
+
+        if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) {
+            return -1;
+        }
+        if (jpc_putdata(enc->out, enc->tmpstream, -1)) {
+            return -1;
+        }
+        enc->len += tilelen;
+
+        jas_stream_close(enc->tmpstream);
+        enc->tmpstream = 0;
+
+        jpc_enc_tile_destroy(enc->curtile);
+        enc->curtile = 0;
+
+    }
+
+    return 0;
+}
+
+
+
+int
+jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr) {
+
+    jpc_enc_t *enc;
+    jpc_enc_cp_t *cp;
+
+    enc = 0;
+    cp = 0;
+
+    jpc_initluts();
+
+    if (!(cp = cp_create(optstr, image))) {
+        fprintf(stderr, "invalid JP encoder options\n");
+        goto error;
+    }
+
+    if (!(enc = jpc_enc_create(cp, out, image))) {
+        goto error;
+    }
+    cp = 0;
+
+    /* Encode the main header. */
+    if (encodemainhdr(enc)) {
+        goto error;
+    }
+
+    /* Encode the main body.  This constitutes most of the encoding work. */
+    if (encodemainbody(enc)) {
+        goto error;
+    }
+
+    /* Write EOC marker segment. */
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) {
+        goto error;
+    }
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write EOI marker\n");
+        goto error;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
+
+    if (jas_stream_flush(enc->out)) {
+        goto error;
+    }
+
+    jpc_enc_destroy(enc);
+
+    return 0;
+
+error:
+    if (cp) {
+        cp_destroy(cp);
+    }
+    if (enc) {
+        jpc_enc_destroy(enc);
+    }
+    return -1;
+}
+
+
+
+/*****************************************************************************\
+* Encoder constructor and destructor.
+\*****************************************************************************/
+
+jpc_enc_t *
+jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image) {
+
+    jpc_enc_t *enc;
+
+    enc = 0;
+
+    if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) {
+        goto error;
+    }
+
+    enc->image = image;
+    enc->out = out;
+    enc->cp = cp;
+    enc->cstate = 0;
+    enc->tmpstream = 0;
+    enc->mrk = 0;
+    enc->curtile = 0;
+
+    if (!(enc->cstate = jpc_cstate_create())) {
+        goto error;
+    }
+    enc->len = 0;
+    enc->mainbodysize = 0;
+
+    return enc;
+
+error:
+
+    if (enc) {
+        jpc_enc_destroy(enc);
+    }
+    return 0;
+}
+
+
+
+void
+jpc_enc_destroy(jpc_enc_t *enc) {
+
+    /* The image object (i.e., enc->image) and output stream object
+       (i.e., enc->out) are created outside of the encoder.
+       Therefore, they must not be destroyed here.
+    */
+
+    if (enc->curtile) {
+        jpc_enc_tile_destroy(enc->curtile);
+    }
+    if (enc->cp) {
+        cp_destroy(enc->cp);
+    }
+    if (enc->cstate) {
+        jpc_cstate_destroy(enc->cstate);
+    }
+    if (enc->tmpstream) {
+        jas_stream_close(enc->tmpstream);
+    }
+
+    jas_free(enc);
+}
+
+
+
+void
+quantize(jas_matrix_t *data, jpc_fix_t stepsize) {
+
+    int i;
+    int j;
+    jpc_fix_t t;
+
+    if (stepsize == jpc_inttofix(1)) {
+        return;
+    }
+
+    for (i = 0; i < jas_matrix_numrows(data); ++i) {
+        for (j = 0; j < jas_matrix_numcols(data); ++j) {
+            t = jas_matrix_get(data, i, j);
+
+{
+    if (t < 0) {
+        t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize));
+    } else {
+        t = jpc_fix_div(t, stepsize);
+    }
+}
+
+            jas_matrix_set(data, i, j, t);
+        }
+    }
+}
+
+
+
 /*****************************************************************************\
 * Tile constructors and destructors.
 \*****************************************************************************/
@@ -2227,7 +2586,7 @@ prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp, jpc_enc_band_t *band) {
         }
 
         prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t));
-        
+
         if (!prc->cblks)
             goto error;
         for (cblkno = 0, cblk = prc->cblks;
@@ -2445,9 +2804,9 @@ jpc_enc_dump(jpc_enc_t *enc) {
                      prcno < rlvl->numprcs;
                      ++prcno, ++prc) {
                     fprintf(stderr, "        prc %5d %5d %5d %5d (%5d %5d)\n",
-                            (int)prc->tlx, (int)prc->tly, 
+                            (int)prc->tlx, (int)prc->tly,
                             (int)prc->brx, (int)prc->bry,
-                            (int)(prc->brx - prc->tlx), 
+                            (int)(prc->brx - prc->tlx),
                             (int)(prc->bry - prc->tly));
                     if (!prc->cblks) {
                         continue;
@@ -2468,352 +2827,6 @@ jpc_enc_dump(jpc_enc_t *enc) {
 
 
 
-static int
-jpc_enc_encodemainbody(jpc_enc_t *enc) {
-
-    int tileno;
-    int i;
-    jpc_sot_t *sot;
-    jpc_enc_tcmpt_t *comp;
-    jpc_enc_tcmpt_t *endcomps;
-    jpc_enc_band_t *band;
-    jpc_enc_band_t *endbands;
-    jpc_enc_rlvl_t *lvl;
-    int rlvlno;
-    jpc_qcc_t *qcc;
-    jpc_cod_t *cod;
-    int adjust;
-    int j;
-    int absbandno;
-    long tilehdrlen;
-    long tilelen;
-    jpc_enc_tile_t *tile;
-    jpc_enc_cp_t *cp;
-    double rho;
-    uint_fast16_t cmptno;
-    int samestepsizes;
-    jpc_enc_ccp_t *ccps;
-    jpc_enc_tccp_t *tccp;
-    int bandno;
-    uint_fast32_t x;
-    uint_fast32_t y;
-    int mingbits;
-    int actualnumbps;
-    jpc_fix_t mxmag;
-    jpc_fix_t mag;
-    int numgbits;
-    const char * error;
-
-    cp = enc->cp;
-
-    for (tileno = 0; tileno < cp->numtiles; ++tileno) {
-        enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno);
-        if (!enc->curtile)
-            abort();
-
-        tile = enc->curtile;
-
-        if (jas_getdbglevel() >= 10) {
-            jpc_enc_dump(enc);
-        }
-
-        endcomps = &tile->tcmpts[tile->numtcmpts];
-        for (cmptno = 0, comp = tile->tcmpts;
-             cmptno < tile->numtcmpts;
-             ++cmptno, ++comp) {
-            if (!cp->ccps[cmptno].sgnd) {
-                adjust = 1 << (cp->ccps[cmptno].prec - 1);
-                for (i = 0; i < jas_matrix_numrows(comp->data); ++i) {
-                    for (j = 0; j < jas_matrix_numcols(comp->data); ++j) {
-                        *jas_matrix_getref(comp->data, i, j) -= adjust;
-                    }
-                }
-            }
-        }
-
-        if (!tile->intmode) {
-            endcomps = &tile->tcmpts[tile->numtcmpts];
-            for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-                jas_matrix_asl(comp->data, JPC_FIX_FRACBITS);
-            }
-        }
-
-        switch (tile->mctid) {
-        case JPC_MCT_RCT:
-            assert(jas_image_numcmpts(enc->image) == 3);
-            jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data,
-                    tile->tcmpts[2].data);
-            break;
-        case JPC_MCT_ICT:
-            assert(jas_image_numcmpts(enc->image) == 3);
-            jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data,
-                    tile->tcmpts[2].data);
-            break;
-        default:
-            break;
-        }
-
-        for (i = 0; i < jas_image_numcmpts(enc->image); ++i) {
-            comp = &tile->tcmpts[i];
-            jpc_tsfb_analyze(comp->tsfb,
-                             ((comp->qmfbid == JPC_COX_RFT) ?
-                              JPC_TSFB_RITIMODE : 0), comp->data);
-
-        }
-
-        endcomps = &tile->tcmpts[tile->numtcmpts];
-        for (cmptno = 0, comp = tile->tcmpts;
-             comp != endcomps;
-             ++cmptno, ++comp) {
-            mingbits = 0;
-            absbandno = 0;
-            /* All bands must have a corresponding quantizer step size,
-               even if they contain no samples and are never coded. */
-            /* Some bands may not be hit by the loop below, so we must
-               initialize all of the step sizes to a sane value. */
-            memset(comp->stepsizes, 0, sizeof(comp->stepsizes));
-            for (rlvlno = 0, lvl = comp->rlvls;
-                 rlvlno < comp->numrlvls;
-                 ++rlvlno, ++lvl) {
-                if (!lvl->bands) {
-                    absbandno += rlvlno ? 3 : 1;
-                    continue;
-                }
-                endbands = &lvl->bands[lvl->numbands];
-                for (band = lvl->bands; band != endbands; ++band) {
-                    if (!band->data) {
-                        ++absbandno;
-                        continue;
-                    }
-                    actualnumbps = 0;
-                    mxmag = 0;
-                    for (y = 0; y < jas_matrix_numrows(band->data); ++y) {
-                        for (x = 0; x < jas_matrix_numcols(band->data); ++x) {
-                            mag = abs(jas_matrix_get(band->data, y, x));
-                            if (mag > mxmag) {
-                                mxmag = mag;
-                            }
-                        }
-                    }
-                    if (tile->intmode) {
-                        actualnumbps =
-                            jpc_firstone(mxmag) + 1;
-                    } else {
-                        actualnumbps =
-                            jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS;
-                    }
-                    numgbits = actualnumbps - (cp->ccps[cmptno].prec - 1 +
-                                               band->analgain);
-                    if (numgbits > mingbits) {
-                        mingbits = numgbits;
-                    }
-                    if (!tile->intmode) {
-                        band->absstepsize =
-                            jpc_fix_div(
-                                jpc_inttofix(1 << (band->analgain + 1)),
-                                band->synweight);
-                    } else {
-                        band->absstepsize = jpc_inttofix(1);
-                    }
-                    band->stepsize = jpc_abstorelstepsize(
-                        band->absstepsize, cp->ccps[cmptno].prec +
-                        band->analgain);
-                    band->numbps = cp->tccp.numgbits +
-                        JPC_QCX_GETEXPN(band->stepsize) - 1;
-
-                    if ((!tile->intmode) && band->data) {
-                        quantize(band->data, band->absstepsize);
-                    }
-
-                    comp->stepsizes[absbandno] = band->stepsize;
-                    ++absbandno;
-                }
-            }
-
-            assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS);
-            if (!tile->intmode) {
-                jas_matrix_divpow2(comp->data,
-                                   JPC_FIX_FRACBITS - JPC_NUMEXTRABITS);
-            } else {
-                jas_matrix_asl(comp->data, JPC_NUMEXTRABITS);
-            }
-        }
-
-        if (mingbits > cp->tccp.numgbits) {
-            fprintf(stderr, "error: too few guard bits (need at least %d)\n",
-                    mingbits);
-            return -1;
-        }
-
-        enc->tmpstream = jas_stream_memopen(0, 0);
-        if (!enc->tmpstream) {
-            fprintf(stderr, "cannot open tmp file\n");
-            return -1;
-        }
-
-        /* Write the tile header. */
-        enc->mrk = jpc_ms_create(JPC_MS_SOT);
-        if (!enc->mrk)
-            return -1;
-        sot = &enc->mrk->parms.sot;
-        sot->len = 0;
-        sot->tileno = tileno;
-        sot->partno = 0;
-        sot->numparts = 1;
-        if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-            fprintf(stderr, "cannot write SOT marker\n");
-            return -1;
-        }
-        jpc_ms_destroy(enc->mrk);
-        enc->mrk = 0;
-
-/************************************************************************/
-/************************************************************************/
-/************************************************************************/
-
-        tccp = &cp->tccp;
-        for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
-            comp = &tile->tcmpts[cmptno];
-            if (comp->numrlvls != tccp->maxrlvls) {
-                if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
-                    return -1;
-                }
-                /* XXX = this is not really correct. we are using comp #0's
-                   precint sizes and other characteristics */
-                comp = &tile->tcmpts[0];
-                cod = &enc->mrk->parms.cod;
-                cod->compparms.csty = 0;
-                cod->compparms.numdlvls = comp->numrlvls - 1;
-                cod->prg = tile->prg;
-                cod->numlyrs = tile->numlyrs;
-                cod->compparms.cblkwidthval =
-                    JPC_COX_CBLKSIZEEXPN(comp->cblkwidthexpn);
-                cod->compparms.cblkheightval =
-                    JPC_COX_CBLKSIZEEXPN(comp->cblkheightexpn);
-                cod->compparms.cblksty = comp->cblksty;
-                cod->compparms.qmfbid = comp->qmfbid;
-                cod->mctrans = (tile->mctid != JPC_MCT_NONE);
-                for (i = 0; i < comp->numrlvls; ++i) {
-                    cod->compparms.rlvls[i].parwidthval =
-                        comp->rlvls[i].prcwidthexpn;
-                    cod->compparms.rlvls[i].parheightval =
-                        comp->rlvls[i].prcheightexpn;
-                }
-                if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-                    return -1;
-                }
-                jpc_ms_destroy(enc->mrk);
-                enc->mrk = 0;
-            }
-        }
-
-        for (cmptno = 0, comp = tile->tcmpts;
-             cmptno < cp->numcmpts;
-             ++cmptno, ++comp) {
-            ccps = &cp->ccps[cmptno];
-            if (ccps->numstepsizes == comp->numstepsizes) {
-                samestepsizes = 1;
-                for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) {
-                    if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) {
-                        samestepsizes = 0;
-                        break;
-                    }
-                }
-            } else {
-                samestepsizes = 0;
-            }
-            if (!samestepsizes) {
-                if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
-                    return -1;
-                }
-                qcc = &enc->mrk->parms.qcc;
-                qcc->compno = cmptno;
-                qcc->compparms.numguard = cp->tccp.numgbits;
-                qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ?
-                    JPC_QCX_SEQNT : JPC_QCX_NOQNT;
-                qcc->compparms.numstepsizes = comp->numstepsizes;
-                qcc->compparms.stepsizes = comp->stepsizes;
-                if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-                    return -1;
-                }
-                qcc->compparms.stepsizes = 0;
-                jpc_ms_destroy(enc->mrk);
-                enc->mrk = 0;
-            }
-        }
-
-        /* Write a SOD marker to indicate the end of the tile header. */
-        if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) {
-            return -1;
-        }
-        if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-            fprintf(stderr, "cannot write SOD marker\n");
-            return -1;
-        }
-        jpc_ms_destroy(enc->mrk);
-        enc->mrk = 0;
-        tilehdrlen = jas_stream_getrwcount(enc->tmpstream);
-
-/************************************************************************/
-/************************************************************************/
-/************************************************************************/
-
-        if (jpc_enc_enccblks(enc)) {
-            abort();
-            return -1;
-        }
-
-        cp = enc->cp;
-        rho = (double) (tile->brx - tile->tlx) * (tile->bry - tile->tly) /
-            ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight -
-                                                   cp->imgareatly));
-        tile->rawsize = cp->rawsize * rho;
-
-        computeLayerSizes(enc, tile, cp, rho, tilehdrlen, &error);
-        
-        if (!error) {
-            int rc;
-            performTier2Coding(enc, tile->numlyrs, tile->lyrsizes, &error);
-
-            rc =  jpc_enc_encodetiledata(enc);
-            if (rc != 0)
-                pm_asprintf(&error, "jpc_enc_encodetiledata() failed\n");
-        }
-
-        if (error) {
-            fprintf(stderr, "%s\n", error);
-            pm_strfree(error);
-            return -1;
-        }
-
-        tilelen = jas_stream_tell(enc->tmpstream);
-
-        if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) {
-            return -1;
-        }
-        jpc_putuint32(enc->tmpstream, tilelen);
-
-        if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) {
-            return -1;
-        }
-        if (jpc_putdata(enc->out, enc->tmpstream, -1)) {
-            return -1;
-        }
-        enc->len += tilelen;
-
-        jas_stream_close(enc->tmpstream);
-        enc->tmpstream = 0;
-
-        jpc_enc_tile_destroy(enc->curtile);
-        enc->curtile = 0;
-
-    }
-
-    return 0;
-}
-
-
-
 /*
  * Copyright (c) 1999-2000 Image Power, Inc. and the University of
  *   British Columbia.
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
index 01e3611e..a884ed4d 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
@@ -50,6 +50,8 @@ jpc_firstone(int_fast32_t const arg) {
 /*---------------------------------------------------------------------------- 
   Calculate the bit position of the first leading one in a nonnegative
   integer.
+
+  LSB is bit position 0.  Iff there are no ones, return -1.
 -----------------------------------------------------------------------------*/
 	int n;
     int x;
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
index e1af0f61..34b9738c 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
@@ -124,6 +124,8 @@
 #include <stdlib.h>
 #include <assert.h>
 
+#include "netpbm/nstring.h"
+
 #include "jasper/jas_fix.h"
 #include "jasper/jas_malloc.h"
 #include "jasper/jas_math.h"
@@ -149,74 +151,111 @@ static int jpc_encrawsigpass(jpc_bitstream_t *out, int bitpos, int,
 static int jpc_encrawrefpass(jpc_bitstream_t *out, int bitpos, int,
   jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec);
 
-/******************************************************************************\
+/*****************************************************************************\
 * Code for encoding code blocks.
-\******************************************************************************/
+\*****************************************************************************/
 
-/* Encode all of the code blocks associated with the current tile. */
-int jpc_enc_enccblks(jpc_enc_t *enc)
-{
-    jpc_enc_tcmpt_t *tcmpt;
-    jpc_enc_tcmpt_t *endcomps;
-    jpc_enc_rlvl_t *lvl;
-    jpc_enc_rlvl_t *endlvls;
-    jpc_enc_band_t *band;
-    jpc_enc_band_t *endbands;
-    jpc_enc_cblk_t *cblk;
-    jpc_enc_cblk_t *endcblks;
-    int i;
-    int j;
-    int mx;
-    int bmx;
-    int v;
-    jpc_enc_tile_t *tile;
-    uint_fast32_t prcno;
-    jpc_enc_prc_t *prc;
+static void
+encodeBlocksOfPrecinct(jpc_enc_prc_t *   const prcP,
+                       jpc_enc_band_t *  const bandP,
+                       jpc_enc_tcmpt_t * const tcmptP,
+                       jpc_enc_t *       const encoderP,
+                       const char **     const errorP) {
 
-    tile = enc->curtile;
+    if (prcP->cblks) {
+        int bmx;
+        uint_fast32_t cblkno;
 
-    endcomps = &tile->tcmpts[tile->numtcmpts];
-    for (tcmpt = tile->tcmpts; tcmpt != endcomps; ++tcmpt) {
-        endlvls = &tcmpt->rlvls[tcmpt->numrlvls];
-        for (lvl = tcmpt->rlvls; lvl != endlvls; ++lvl) {
-            if (!lvl->bands) {
-                continue;
-            }
-            endbands = &lvl->bands[lvl->numbands];
-            for (band = lvl->bands; band != endbands; ++band) {
-                if (!band->data) {
-                    continue;
+        for (cblkno = 0, bmx = 0; cblkno < prcP->numcblks; ++ cblkno) {
+            jpc_enc_cblk_t * const cblkP = &prcP->cblks[cblkno];
+
+            int mx;
+            uint_fast32_t row;
+
+            for (row = 0, mx = 0;
+                 row < jas_matrix_numrows(cblkP->data);
+                 ++row) {
+
+                uint_fast32_t col;
+
+                for (col = 0; col < jas_matrix_numcols(cblkP->data); ++col) {
+                    int const v = abs(jas_matrix_get(cblkP->data, row, col));
+                    if (v > mx)
+                        mx = v;
                 }
-                for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
-                    if (!prc->cblks) {
-                        continue;
-                    }
-                    bmx = 0;
-                    endcblks = &prc->cblks[prc->numcblks];
-                    for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-                        mx = 0;
-                        for (i = 0; i < jas_matrix_numrows(cblk->data); ++i) {
-                            for (j = 0; j < jas_matrix_numcols(cblk->data); ++j) {
-                                v = abs(jas_matrix_get(cblk->data, i, j));
-                                if (v > mx) {
-                                    mx = v;
-                                }
-                            }
-                        }
-                        if (mx > bmx) {
-                            bmx = mx;
-                        }
-                        cblk->numbps = JAS_MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0);
-                    }
+            }
+            if (mx > bmx)
+                bmx = mx;
 
-                    for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-                        cblk->numimsbs = band->numbps - cblk->numbps;
-                        assert(cblk->numimsbs >= 0);
-                    }
+            cblkP->numbps = MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0);
+        }
+
+        for (cblkno = 0; cblkno < prcP->numcblks; ++ cblkno) {
+            jpc_enc_cblk_t * const cblkP = &prcP->cblks[cblkno];
 
-                    for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-                        if (jpc_enc_enccblk(enc, cblk->stream, tcmpt, band, cblk)) {
-                            return -1;
+            assert(cblkP->numbps <= bandP->numbps);
+        }
+
+        for (cblkno = 0, *errorP = NULL;
+             cblkno < prcP->numcblks && !*errorP;
+             ++ cblkno) {
+
+            jpc_enc_cblk_t * const cblkP = &prcP->cblks[cblkno];
+
+            int rc;
+
+            rc = jpc_enc_enccblk(encoderP,
+                                 cblkP->stream, tcmptP, bandP, cblkP);
+            if (rc != 0)
+                pm_asprintf(errorP, "Encoding failed on code block %u "
+                            "of %u", (unsigned)cblkno,
+                            (unsigned)prcP->numcblks);
+        }
+    } else
+        *errorP = NULL;
+}
+
+
+
+int
+jpc_enc_enccblks(jpc_enc_t * const encoderP) {
+/*----------------------------------------------------------------------------
+  Encode all of the code blocks associated with the current tile.
+-----------------------------------------------------------------------------*/
+    jpc_enc_tile_t * const tileP = encoderP->curtile;
+
+    uint_fast32_t cmptno;
+
+    for (cmptno = 0; cmptno < tileP->numtcmpts; ++cmptno) {
+        jpc_enc_tcmpt_t * const tcmptP = &tileP->tcmpts[cmptno];
+
+        unsigned int lvlno;
+        for (lvlno = 0; lvlno < tcmptP->numrlvls; ++ lvlno) {
+            jpc_enc_rlvl_t * const lvlP = &tcmptP->rlvls[lvlno];
+
+            if (lvlP->bands) {
+                uint_fast32_t bandno;
+
+                for (bandno = 0; bandno < lvlP->numbands; ++bandno) {
+                    jpc_enc_band_t * const bandP = &lvlP->bands[bandno];
+
+                    if (bandP->data) {
+                        uint_fast32_t prcno;
+
+                        for (prcno = 0; prcno < lvlP->numprcs; ++prcno) {
+
+                            const char * error;
+
+                            encodeBlocksOfPrecinct(&bandP->prcs[prcno],
+                                                   bandP,
+                                                   tcmptP,
+                                                   encoderP,
+                                                   &error);
+
+                            if (error) {
+                                pm_strfree(error);
+                                return -1;
+                            }
                         }
                     }
                 }
@@ -226,6 +265,8 @@ int jpc_enc_enccblks(jpc_enc_t *enc)
     return 0;
 }
 
+
+
 static int getthebyte(jas_stream_t *in, long off)
 {
     int c;
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
index fede2bef..e6e3942a 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
@@ -114,9 +114,9 @@
  * $Id$
  */
 
-/******************************************************************************\
+/*****************************************************************************\
 * Includes
-\******************************************************************************/
+\*****************************************************************************/
 
 #include <assert.h>
 #include <stdio.h>
@@ -133,21 +133,21 @@
 #include "jpc_flt.h"
 #include "jpc_util.h"
 
-/******************************************************************************\
+/*****************************************************************************\
 * Miscellaneous Functions
-\******************************************************************************/
+\*****************************************************************************/
 
-int jpc_atoaf(const char *s, int *numvalues, double **values)
-{
-	static char delim[] = ", \t\n";
+static unsigned int
+countOfTokens(const char * const s,
+			  const char * const delim) {
+
+	unsigned int n;
 	char buf[4096];
-	int n;
-	double *vs;
-	char *cp;
+	const char * cp;
 
 	strncpy(buf, s, sizeof(buf));
 	buf[sizeof(buf) - 1] = '\0';
-	n = 0;
+	n = 0;  /* initial value */
 	if ((cp = strtok(buf, delim))) {
 		++n;
 		while ((cp = strtok(0, delim))) {
@@ -156,32 +156,57 @@ int jpc_atoaf(const char *s, int *numvalues, double **values)
 			}
 		}
 	}
+	return n;
+}
+
+
+
+int
+jpc_atoaf(const char * const s,
+		  int *        const numvaluesP,
+		  double **    const valuesP) {
+/*----------------------------------------------------------------------------
+   Parse a string like "3.2,9,-5".  Return as *numvaluesP the number of
+   values in the string and as *valuesP a malloced array of the values.
 
-	if (n) {
-		if (!(vs = jas_malloc(n * sizeof(double)))) {
+   But if the string is empty (*numvaluesP is zero), return *valuesP NULL.
+
+   Delimiters can be comma as in the example or space, tab, or newline.
+-----------------------------------------------------------------------------*/
+	char const delim[] = ", \t\n";
+
+	unsigned int const valueCt = countOfTokens(s, delim);
+
+	if (valueCt > 0) {
+		unsigned int i;
+		double * vs;
+		const char * cp;
+		char buf[4096];
+
+		if (!(vs = jas_malloc(valueCt * sizeof(double)))) {
 			return -1;
 		}
 
 		strncpy(buf, s, sizeof(buf));
 		buf[sizeof(buf) - 1] = '\0';
-		n = 0;
+		i = 0;
 		if ((cp = strtok(buf, delim))) {
-			vs[n] = atof(cp);
-			++n;
+			vs[i] = atof(cp);
+			++i;
 			while ((cp = strtok(0, delim))) {
 				if (cp[0] != '\0') {
-					vs[n] = atof(cp);
-					++n;
+					vs[i] = atof(cp);
+					++i;
 				}
 			}
 		}
+		assert(i == valueCt);
+		*numvaluesP = valueCt;
+		*valuesP    = vs;
 	} else {
-		vs = 0;
+		*valuesP    = NULL;
+		*numvaluesP = 0;
 	}
-
-	*numvalues = n;
-	*values = vs;
-
 	return 0;
 }
 
diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
index ee5189ce..8f7409c0 100644
--- a/converter/other/jpeg2000/pamtojpeg2k.c
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -53,6 +53,8 @@ struct CmdlineInfo {
     enum compmode compmode;
     unsigned int compressionSpec;
     float        compression;
+    unsigned int size;
+    unsigned int sizeSpec;
     char *       ilyrrates;
     enum progression progression;
     unsigned int numrlvls;
@@ -76,7 +78,7 @@ parseCommandLine(int argc, char ** argv,
                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
-   *cmdline_p structure are actually in the supplied argv array.  And
+   *cmdlineP structure are actually in the supplied argv array.  And
    sometimes, one of these strings is actually just a suffix of an entry
    in argv!
 -----------------------------------------------------------------------------*/
@@ -126,6 +128,8 @@ parseCommandLine(int argc, char ** argv,
             &modeSpec,           0);
     OPTENT3(0, "compression",  OPT_FLOAT,  &cmdlineP->compression,
             &cmdlineP->compressionSpec,    0);
+    OPTENT3(0, "size",         OPT_UINT,   &cmdlineP->size,
+            &cmdlineP->sizeSpec,           0);
     OPTENT3(0, "ilyrrates",    OPT_STRING, &cmdlineP->ilyrrates,
             &ilyrratesSpec,      0);
     OPTENT3(0, "progression",  OPT_STRING, &progressionOpt,
@@ -219,6 +223,9 @@ parseCommandLine(int argc, char ** argv,
     if (!debuglevelSpec)
         cmdlineP->debuglevel = 0;
 
+    if (cmdlineP->compressionSpec && cmdlineP->sizeSpec)
+        pm_error("You cannot specify by -compression and -size");
+
     if (argc - 1 == 0)
         cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
@@ -405,6 +412,8 @@ writeJpc(jas_image_t *      const jasperP,
 
     if (cmdline.compressionSpec)
         sprintf(rateOpt, "rate=%1.9f", 1.0/cmdline.compression);
+    else if (cmdline.sizeSpec)
+        sprintf(rateOpt, "rate=%uB", cmdline.size);
     else {
         /* No 'rate' option.  This means there is no constraint on the image
            size, so the encoder will compress losslessly.  Note that the
diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c
index f5a0d39d..09e35410 100644
--- a/converter/ppm/ppmtompeg/frame.c
+++ b/converter/ppm/ppmtompeg/frame.c
@@ -12,7 +12,7 @@
  *  Frame_AllocYCC                               *
  *  Frame_AllocDecoded                           *
  *  Frame_AllocHalf                                  *
- *  Frame_Resize                                     * 
+ *  Frame_Resize                                     *
  *                                       *
  *===========================================================================*/
 
@@ -52,7 +52,7 @@
  *===========*/
 
 /* The maximum number of B-Frames allowed between reference frames. */
-#define  B_FRAME_RUN  16    
+#define  B_FRAME_RUN  16
 
 /*==================*
  * GLOBAL VARIABLES *
@@ -64,13 +64,13 @@ static unsigned int numOfFrames;
 
 /*====================================================
 * Resize_Array_Width
-*    
+*
 *   This function will resize any array width up
 * or down in size.  The algorithm is based on the
 * least common multiple approach more commonly
 * used in audio frequency adjustments.
 *=====================================================*/
-static void 
+static void
 Resize_Array_Width(uint8 ** const inarray,
                    int      const in_x,
                    int      const in_y,
@@ -86,7 +86,7 @@ Resize_Array_Width(uint8 ** const inarray,
     uint8 pointA,pointB;
     double slope,diff;
 #endif
-    
+
     for (i = 0; i < in_y; ++i) {     /* For each row */
         unsigned int j;
         inptr = &inarray[i][0];
@@ -106,7 +106,7 @@ Resize_Array_Width(uint8 ** const inarray,
                     in_total = in_total - out_x;
                     --inptr;
                 }
-            } else {  
+            } else {
 #ifdef DOING_INTERPOLATION
                 pointA = *inptr;
 #endif
@@ -124,10 +124,10 @@ Resize_Array_Width(uint8 ** const inarray,
                 } else {
                     *outptr = (pointB -
                                (uint8)(slope*(((float)(out_x)) - diff)));
-                } 
+                }
 #endif
                 /* Non-Interpolative solution */
-                *outptr = *inptr;  
+                *outptr = *inptr;
 
                 ++outptr;
                 out_total = out_total + in_x;
@@ -153,7 +153,7 @@ Resize_Array_Width(uint8 ** const inarray,
 * Same as Resize_array_Width except pointer
 * manipulation must change.
 *===============================*/
-static void 
+static void
 Resize_Array_Height(uint8 ** const inarray,
                     int      const in_x,
                     int      const in_y,
@@ -176,7 +176,7 @@ Resize_Array_Height(uint8 ** const inarray,
         out_total = 0;
         k = 0;
         for(j=0; j < out_y; ++j){  /* for each output value */
-            if (in_total == out_total) {  
+            if (in_total == out_total) {
                 outarray[j][i] = inarray[k][i];
                 out_total=out_total+in_y;
                 while(in_total < out_total){
@@ -187,7 +187,7 @@ Resize_Array_Height(uint8 ** const inarray,
                     in_total = in_total - out_y;
                     --k;
                 }
-            } else {  
+            } else {
 #ifdef DOING_INTERPOLATION
                 pointA = inarray[k][i];
                 if (k != (in_y - 1)) {
@@ -210,7 +210,7 @@ Resize_Array_Height(uint8 ** const inarray,
                     in_total = in_total - out_y;
                     --k;
                 }
-            } 
+            }
         }
     }
 }
@@ -220,7 +220,7 @@ Resize_Array_Height(uint8 ** const inarray,
 /*========================================================
 * Resize_Width
 *======================================================*/
-static void  
+static void
 Resize_Width(MpegFrame * const omfrw,
              MpegFrame * const mfrw,
              int         const in_x,
@@ -274,12 +274,12 @@ Resize_Width(MpegFrame * const omfrw,
             free(mfrw->orig_y[i]);
         }
         free(mfrw->orig_y);
-        
+
         for (i = 0; i < in_y / 2; ++i) {
             free(mfrw->orig_cr[i]);
         }
         free(mfrw->orig_cr);
-        
+
         for (i = 0; i < in_y / 2; ++i) {
             free(mfrw->orig_cb[i]);
         }
@@ -300,9 +300,9 @@ Resize_Height(MpegFrame * const omfrh,
               int         const in_x,
               int         const in_y,
               int         const out_y) {
-    
-    unsigned int y; 
-    
+
+    unsigned int y;
+
     Fsize_y = out_y;
 
     /* Allocate new frame memory */
@@ -347,12 +347,12 @@ Resize_Height(MpegFrame * const omfrh,
             free(mfrh->orig_y[i]);
         }
         free(mfrh->orig_y);
-        
+
     for (i = 0; i < in_y / 2; ++i) {
         free(mfrh->orig_cr[i]);
     }
     free(mfrh->orig_cr);
-    
+
     for (i = 0; i < in_y / 2; ++i) {
         free(mfrh->orig_cb[i]);
     }
@@ -386,8 +386,8 @@ Frame_Init(unsigned int const numOfFramesRequested) {
     for (idx = 0; idx < numOfFrames; ++idx) {
         MALLOCVAR(frameMemory[idx]);
         frameMemory[idx]->inUse = FALSE;
-        frameMemory[idx]->orig_y = NULL;    
-        frameMemory[idx]->y_blocks = NULL; 
+        frameMemory[idx]->orig_y = NULL;
+        frameMemory[idx]->y_blocks = NULL;
         frameMemory[idx]->decoded_y = NULL;
         frameMemory[idx]->halfX = NULL;
         frameMemory[idx]->next = NULL;
@@ -458,11 +458,11 @@ FreeFrame(MpegFrame * const frameP) {
             for ( i = 0; i < Fsize_y; ++i )
                 free(frameP->halfX[i]);
             free(frameP->halfX);
-            
+
             for (i = 0; i < Fsize_y-1; ++i)
                 free(frameP->halfY[i]);
             free(frameP->halfY);
-            
+
             for (i = 0; i < Fsize_y-1; ++i)
                 free(frameP->halfBoth[i]);
             free(frameP->halfBoth);
@@ -542,7 +542,7 @@ GetUnusedFrame() {
                 "See the man page for help.\n");
         exit(1);
     }
-    return frameMemory[idx]; 
+    return frameMemory[idx];
 }
 
 
@@ -638,7 +638,7 @@ Frame_AllocBlocks(MpegFrame * const frameP) {
             MALLOCARRAY(frameP->y_blocks[i], dctx);
             ERRCHK(frameP->y_blocks[i], "malloc");
         }
-    
+
         MALLOCARRAY(frameP->cr_blocks, dcty / 2);
         ERRCHK(frameP->cr_blocks, "malloc");
         MALLOCARRAY(frameP->cb_blocks, dcty / 2);
@@ -672,7 +672,7 @@ Frame_AllocYCC(MpegFrame * const frameP) {
         /* already allocated */
     } else {
         unsigned int y;
-    
+
         DBG_PRINT(("ycc_calc:\n"));
         /*
          * first, allocate tons of memory
@@ -755,7 +755,7 @@ Frame_AllocHalf(MpegFrame * const frameP) {
  *
  *  allocate memory for decoded frame for the given frame, if required
  *  if makeReference == TRUE, then makes it reference frame
- * 
+ *
  * RETURNS: nothing
  *
  * SIDE EFFECTS:    none
@@ -781,14 +781,14 @@ Frame_AllocDecoded(MpegFrame * const frameP,
             MALLOCARRAY(frameP->decoded_y[y], Fsize_x);
             ERRCHK(frameP->decoded_y[y], "malloc");
         }
-        
+
         MALLOCARRAY(frameP->decoded_cr, Fsize_y / 2);
         ERRCHK(frameP->decoded_cr, "malloc");
         for (y = 0; y < (Fsize_y / 2); ++y) {
             MALLOCARRAY(frameP->decoded_cr[y], Fsize_x / 2);
             ERRCHK(frameP->decoded_cr[y], "malloc");
         }
-        
+
         MALLOCARRAY(frameP->decoded_cb, Fsize_y / 2);
         ERRCHK(frameP->decoded_cb, "malloc");
         for (y = 0; y < (Fsize_y / 2); ++y) {
@@ -810,7 +810,7 @@ Frame_AllocDecoded(MpegFrame * const frameP,
  *
  * Frame_Resize                  by James Boucher
  *                Boston University Multimedia Communications Lab
- *  
+ *
  *     This function takes the mf input frame, read in READFrame(),
  * and resizes all the input component arrays to the output
  * dimensions specified in the parameter file as OUT_SIZE.
@@ -828,11 +828,11 @@ Frame_Resize(MpegFrame * const omf,
     MpegFrame * frameAP;  /* intermediate frame */
 
     MALLOCVAR_NOFAIL(frameAP);
-    
+
     if (insize_x != outsize_x && insize_y != outsize_y) {
         Resize_Width(frameAP, mf, insize_x, insize_y, outsize_x);
         Resize_Height(omf, frameAP, outsize_x, insize_y, outsize_y);
-    } else 
+    } else
         if (insize_x ==outsize_x && insize_y != outsize_y) {
             Resize_Height(omf, mf, insize_x, insize_y, outsize_y);
         } else
@@ -843,3 +843,5 @@ Frame_Resize(MpegFrame * const omf,
 
     free(frameAP);
 }
+
+
diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c
index 2a359b2f..dcc02052 100644
--- a/converter/ppm/ppmtompeg/readframe.c
+++ b/converter/ppm/ppmtompeg/readframe.c
@@ -1,13 +1,13 @@
 /*===========================================================================*
- * readframe.c                    
- *                                
- *  procedures to read in frames  
- *                                
- * EXPORTED PROCEDURES:           
- *  ReadFrame                     
- *  SetFileType                   
- *  SetFileFormat                 
- *                                
+ * readframe.c
+ *
+ *  procedures to read in frames
+ *
+ * EXPORTED PROCEDURES:
+ *  ReadFrame
+ *  SetFileType
+ *  SetFileFormat
+ *
  *===========================================================================*/
 
 /* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
@@ -62,7 +62,7 @@ struct YuvLine {
 #ifdef __OS2__
   #define popen _popen
 #endif
-   
+
 
 /*==================*
  * Global VARIABLES *
@@ -100,7 +100,7 @@ static void DoKillDim (MpegFrame *mf, int w, int h);
 
 
 
-void    
+void
 SetResize(bool const set) {
     resizeFrame = set;
 }
@@ -134,7 +134,7 @@ ReadPNM(MpegFrame * const mpegFrameP,
 static void
 openFile(struct inputSource * const inputSourceP,
          unsigned int         const frameNumber,
-         const char *         const conversion, 
+         const char *         const conversion,
          FILE **              const ifPP) {
 
     if (inputSourceP->stdinUsed) {
@@ -145,24 +145,24 @@ openFile(struct inputSource * const inputSourceP,
                 "INPUT_CONVERTER * in the parameter file or supply the "
                 "frames in files by specifying a directory with "
                 "INPUT_DIRECTORY in the parameter file.");
-        
+
         *ifPP = stdin;
     } else {
         const char * fileName;
         const char * fullFileName;
-        
+
         GetNthInputFileName(inputSourceP, frameNumber, &fileName);
-        
+
         pm_asprintf(&fullFileName, "%s/%s", currentPath, fileName);
-        
+
         CurrFile = fullFileName;
-        
+
         if (fileType == ANY_FILE_TYPE) {
             char command[1024];
             const char * convertPtr;
             char * commandPtr;
             const char * charPtr;
-            
+
             /* replace every occurrence of '*' with fullFileName */
             convertPtr = conversion;
             commandPtr = command;
@@ -172,7 +172,7 @@ openFile(struct inputSource * const inputSourceP,
                     ++commandPtr;
                     ++convertPtr;
                 }
-                
+
                 if (*convertPtr == '*') {
                     /* copy fullFileName */
                     charPtr = fullFileName;
@@ -185,7 +185,7 @@ openFile(struct inputSource * const inputSourceP,
                 }
             }
             *commandPtr = '\0';
-            
+
             *ifPP = popen(command, "r");
             if (*ifPP == NULL) {
                 pm_message(
@@ -201,7 +201,7 @@ openFile(struct inputSource * const inputSourceP,
             *ifPP = fopen(fullFileName, "rb");
             if (*ifPP == NULL)
                 pm_error("Couldn't open input file '%s'", fullFileName);
-            
+
             if (baseFormat == JMOVIE_FILE_TYPE)
                 unlink(fullFileName);
         }
@@ -238,7 +238,7 @@ fileIsAtEnd(FILE * const ifP) {
 
     c = getc(ifP);
     if (c == EOF) {
-        if (feof(ifP)) 
+        if (feof(ifP))
             eof = TRUE;
         else
             pm_error("File error on getc() to position to image");
@@ -248,7 +248,7 @@ fileIsAtEnd(FILE * const ifP) {
         eof = FALSE;
 
         rc = ungetc(c, ifP);
-        if (rc == EOF) 
+        if (rc == EOF)
             pm_error("File error doing ungetc() to position to image.");
     }
     return eof;
@@ -262,14 +262,11 @@ ReadFrameFile(MpegFrame *  const frameP,
               const char * const conversion,
               bool *       const eofP) {
 /*----------------------------------------------------------------------------
-   Read a frame from the file 'ifP'.
+   Read a frame from the file 'ifP', doing adjustments as indicated.
 
    Return *eofP == TRUE iff we encounter EOF before we can get the
    frame.
 -----------------------------------------------------------------------------*/
-    MpegFrame   tempFrame;
-    MpegFrame * framePtr;
-
     /* To make this code fit Netpbm properly, we should remove handling
        of all types except PNM and use pm_nextimage() to handle sensing
        of end of stream.
@@ -278,53 +275,59 @@ ReadFrameFile(MpegFrame *  const frameP,
     if (fileIsAtEnd(ifP))
         *eofP = TRUE;
     else {
+        MpegFrame   preResizeFrame;
+            /* The frame object that holds the frame before resizing */
+        MpegFrame * rawFrameP;
+            /* The frame object with which we read in the raw frame */
+
         *eofP = FALSE;
 
         if (resizeFrame) {
-            tempFrame.inUse = FALSE;
-            tempFrame.orig_y = NULL;
-            tempFrame.y_blocks = NULL;
-            tempFrame.decoded_y = NULL;
-            tempFrame.halfX = NULL;
-            framePtr = &tempFrame;
+            preResizeFrame.inUse = FALSE;
+            preResizeFrame.orig_y = NULL;
+            preResizeFrame.y_blocks = NULL;
+            preResizeFrame.decoded_y = NULL;
+            preResizeFrame.halfX = NULL;
+            rawFrameP = &preResizeFrame;
         } else
-            framePtr = frameP;
+            rawFrameP = frameP;
 
         switch(baseFormat) {
         case YUV_FILE_TYPE:
 
             /* Encoder YUV */
             if ((strncmp (yuvConversion, "EYUV", 4) == 0) ||
-                (strncmp (yuvConversion, "UCB", 3) == 0)) 
+                (strncmp (yuvConversion, "UCB", 3) == 0))
 
-                ReadEYUV(framePtr, ifP, realWidth, realHeight);
+                ReadEYUV(rawFrameP, ifP, realWidth, realHeight);
 
             else
                 /* Abekas-type (interlaced) YUV */
-                ReadAYUV(framePtr, ifP, realWidth, realHeight);
+                ReadAYUV(rawFrameP, ifP, realWidth, realHeight);
 
             break;
         case Y_FILE_TYPE:
-            ReadY(framePtr, ifP, realWidth, realHeight);
+            ReadY(rawFrameP, ifP, realWidth, realHeight);
             break;
         case PNM_FILE_TYPE:
-            ReadPNM(framePtr, ifP);
+            ReadPNM(rawFrameP, ifP);
             break;
         case SUB4_FILE_TYPE:
-            ReadSub4(framePtr, ifP, yuvWidth, yuvHeight);
+            ReadSub4(rawFrameP, ifP, yuvWidth, yuvHeight);
             break;
         case JPEG_FILE_TYPE:
         case JMOVIE_FILE_TYPE:
-            ReadJPEG(framePtr, ifP);
+            ReadJPEG(rawFrameP, ifP);
             break;
         default:
             break;
         }
 
-        if (resizeFrame)
-            Frame_Resize(frameP, &tempFrame, Fsize_x, Fsize_y, 
+        if (resizeFrame) {
+            assert(rawFrameP == &preResizeFrame);
+            Frame_Resize(frameP, &preResizeFrame, Fsize_x, Fsize_y,
                          outputWidth, outputHeight);
-    
+        }
         if (GammaCorrection)
             DoGamma(frameP, Fsize_x, Fsize_y);
 
@@ -338,7 +341,7 @@ ReadFrameFile(MpegFrame *  const frameP,
 
 
 void
-ReadFrame(MpegFrame *          const frameP, 
+ReadFrame(MpegFrame *          const frameP,
           struct inputSource * const inputSourceP,
           unsigned int         const frameNumber,
           const char *         const conversion,
@@ -693,7 +696,7 @@ SeparateLine(fpointer, lineptr, width)
         fprintf(stderr, "       or any even-length string consisting of the letters U, V, and Y.\n");
             exit(1);
         }
-    
+
     }
 
 }
@@ -738,7 +741,7 @@ ReadY(mf, fpointer, width, height)
     for (y = Fsize_y; y < height; y++) {
     safe_fread(junk, 1, width, fpointer);
     }
-    
+
     for (y = 0 ; y < (Fsize_y >> 1); y++) {
       memset(mf->orig_cb[y], 128, (Fsize_x>>1));
       memset(mf->orig_cr[y], 128, (Fsize_x>>1));
@@ -830,7 +833,7 @@ int w,h;
   int i,j;
 
   if (!init_done) {
-    for(i=0; i<256; i++) 
+    for(i=0; i<256; i++)
       GammaVal[i]=(unsigned char) (pow(((double) i)/255.0,GammaValue)*255.0+0.5);
     init_done=TRUE;
   }
@@ -871,7 +874,7 @@ int w,h;
                         ^ kill_dim_break
                              ^kill_dim_end
               kill_dim_slope gives the slope (y = kill_dim_slope * x +0)
-              from 0 to kill_dim_break                      
+              from 0 to kill_dim_break
  *
  *===========================================================================*/
 
@@ -930,7 +933,7 @@ int w,h;
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-/*  
+/*
  *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/readframe.c,v 1.27 1995/08/14 22:31:40 smoot Exp $
  *  $Log: readframe.c,v $
  *  Revision 1.27  1995/08/14 22:31:40  smoot
diff --git a/doc/HISTORY b/doc/HISTORY
index 843d8624..2cd5c1c6 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,23 +4,25 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-22.12.24 BJH  Release 11.00.03
+22.12.31 BJH  Release 11.01.00
+
+              pamcat: Add -listfile .
+
+              pamtojpeg2k: add -size option.  Doesn't precisely work (and
+              -compression has never worked precisely either); should be
+              fixed some day.
 
               ppmtompeg: Fix crash with resize option because of invalid
               memory free.
 
-22.11.14 BJH  Release 11.00.02
+              fitstopnm: fix invalid memory reference (nonterminated ASCIIZ
+              string).
 
               packaging: Fix library minor number to 100 + Netpbm minor number
               so it is higher than previous ones in library major 100.
               I.e. libnetpbm.so.100.101 instead of libnetpbm.so.100.1.
               Introduced in Netpbm 11.00.00.
 
-22.10.14 BJH  Release 11.00.01
-
-              fitstopnm: fix invalid memory reference (nonterminated ASCIIZ
-              string).
-
 22.09.28 BJH  Release 11.00.00
 
               (No significance to new major number; just ran out of 2-digit
@@ -147,7 +149,7 @@ CHANGE HISTORY
 
 21.12.27 BJH  Release 10.97.00
 
-              Add pbnnoise.
+              Add pbmnoise.
 
               pnmpad: Use -halign with -mwidth and default to centering the
               image instead of left-justifying when no other padding is being
diff --git a/editor/pamcat.c b/editor/pamcat.c
index c7f0482d..7823bdfc 100644
--- a/editor/pamcat.c
+++ b/editor/pamcat.c
@@ -9,6 +9,8 @@
 
 =============================================================================*/
 
+#include <stdio.h>
+#include <limits.h>
 #include <assert.h>
 
 #include "pm_c_util.h"
@@ -36,12 +38,43 @@ enum Orientation {TOPBOTTOM, LEFTRIGHT};
 enum Justification {JUST_CENTER, JUST_MIN, JUST_MAX};
   /* Justification of images in concatenation */
 
+/* FOPEN_MAX is usually defined in stdio.h, PATH_MAX in limits.h
+   Given below are typical values.  Adjust as necessary.
+ */
+
+#ifndef FOPEN_MAX
+  #define FOPEN_MAX 16
+#endif
+
+#ifndef PATH_MAX
+  #define PATH_MAX 255
+#endif
+
+
+static const char **
+copyOfStringList(const char ** const list,
+                 unsigned int  const size) {
+
+    const char ** retval;
+    unsigned int i;
+
+    MALLOCARRAY_NOFAIL(retval, size);
+
+    for (i = 0; i < size; ++i)
+        retval[i] = pm_strdup(list[i]);
+
+    return retval;
+}
+
+
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char **       inputFileName;
-    unsigned int        fileCt;
+    unsigned int        inputFileCt;
+    const char *        listfile;  /* NULL if not specified */
     enum PadColorMethod padColorMethod;
     enum Orientation    orientation;
     enum Justification  justification;
@@ -49,7 +82,6 @@ struct CmdlineInfo {
 };
 
 
-
 static void
 parseCommandLine(int argc, const char ** const argv,
                  struct CmdlineInfo * const cmdlineP) {
@@ -67,6 +99,7 @@ parseCommandLine(int argc, const char ** const argv,
     unsigned int leftright, topbottom;
     unsigned int black, white;
     unsigned int jtop, jbottom, jleft, jright, jcenter;
+    unsigned int listfileSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -82,6 +115,8 @@ parseCommandLine(int argc, const char ** const argv,
     OPTENT3(0, "jleft",       OPT_FLAG,   NULL, &jleft,             0);
     OPTENT3(0, "jright",      OPT_FLAG,   NULL, &jright,            0);
     OPTENT3(0, "jcenter",     OPT_FLAG,   NULL, &jcenter,           0);
+    OPTENT3(0, "listfile",    OPT_STRING, &cmdlineP->listfile,
+            &listfileSpec,      0);
     OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose, 0);
 
     opt.opt_table = option_def;
@@ -148,27 +183,144 @@ parseCommandLine(int argc, const char ** const argv,
         }
     }
 
-    if (argc-1 < 1) {
-        MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, 1);
-        cmdlineP->inputFileName[0] = "-";
-        cmdlineP->fileCt = 1;
+    if (listfileSpec) {
+        if (argc-1 > 0)
+          pm_error ("You can not specify files on the command line and "
+                    "also -listfile.");
     } else {
-        unsigned int i;
-        unsigned int stdinCt;
+        cmdlineP->listfile = NULL;
+
+        if (argc-1 < 1) {
+            MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, 1);
+            cmdlineP->inputFileName[0] = "-";
+            cmdlineP->inputFileCt = 1;
+        } else {
+            unsigned int i;
+            unsigned int stdinCt;
             /* Number of input files user specified as Standard Input */
 
-        MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, argc-1);
+            MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, argc-1);
+
+            for (i = 0, stdinCt = 0; i < argc-1; ++i) {
+                cmdlineP->inputFileName[i] = argv[1+i];
+                if (streq(argv[1+i], "-"))
+                    ++stdinCt;
+            }
+            cmdlineP->inputFileCt = argc-1;
+            if (stdinCt > 1)
+                pm_error("At most one input image can come from "
+                         "Standard Input.  You specified %u", stdinCt);
+        }
+    }
+}
+
+
+
+static void
+freeCmdLine(struct CmdlineInfo const cmdline) {
+
+    if (!cmdline.listfile)
+        free(cmdline.inputFileName);
+}
+
+
 
-        for (i = 0, stdinCt = 0; i < argc-1; ++i) {
-            cmdlineP->inputFileName[i] = argv[1+i];
-            if (streq(argv[1+i], "-"))
-                ++stdinCt;
+static void
+createInFileListFmFile(const char  *        const listFileNm,
+                       bool                 const verbose,
+                       const char ***       const inputFileNmP,
+                       unsigned int *       const inputFileCtP) {
+
+    FILE * const lfP = pm_openr(listFileNm);
+
+    const char ** inputFileNm;
+    unsigned int inputFileCt;
+    unsigned int emptyLineCt;
+    unsigned int stdinCt;
+    int eof;
+
+    MALLOCARRAY_NOFAIL(inputFileNm, FOPEN_MAX);
+
+    for (inputFileCt = emptyLineCt = stdinCt = eof = 0; !eof; ) {
+
+        size_t lineLen;
+        char * buf;
+        size_t bufferSz;
+
+        buf = NULL;  /* initial value */
+        bufferSz = 0;  /* initial value */
+
+        pm_getline(lfP, &buf, &bufferSz, &eof, &lineLen);
+
+        if (!eof) {
+            if (lineLen == 0)
+                ++emptyLineCt;
+            else if (lineLen > PATH_MAX)
+                pm_error("Path/file name in list file is too long "
+                         "(%u bytes).  Maximum is %u bytes",
+                         (unsigned)lineLen, PATH_MAX);
+            else /* 0 < lineLen < PATH_MAX */ {
+                if (inputFileCt >= FOPEN_MAX)
+                    pm_error("Too many files in list file.  Maximum is %u",
+                             FOPEN_MAX);
+                else {
+                    inputFileNm[inputFileCt] = buf;
+                    ++inputFileCt;
+                    if (streq(buf, "-"))
+                        ++stdinCt;
+                }
+            }
         }
-        cmdlineP->fileCt = argc-1;
-        if (stdinCt > 1)
-            pm_error("At most one input image can come from Standard Input.  "
-                     "You specified %u", stdinCt);
     }
+
+    pm_close(lfP);
+
+    if (stdinCt > 1)
+        pm_error("At most one input image can come from Standard Input.  "
+                 "You specified %u", stdinCt);
+
+    if (inputFileCt == 0)
+        pm_error("No files specified in list file.");
+
+    if (verbose) {
+        pm_message("%u files specified and %u blank lines in list file",
+                   inputFileCt, emptyLineCt);
+    }
+
+    *inputFileCtP = inputFileCt;
+    *inputFileNmP = inputFileNm;
+}
+
+
+
+static void
+createInFileList(struct CmdlineInfo const cmdline,
+                 bool               const verbose,
+                 const char ***     const inputFileNmP,
+                 unsigned int *     const inputFileCtP) {
+
+    if (cmdline.listfile)
+        createInFileListFmFile(cmdline.listfile, verbose,
+                               inputFileNmP, inputFileCtP);
+    else {
+        *inputFileCtP = cmdline.inputFileCt;
+        *inputFileNmP = copyOfStringList(cmdline.inputFileName,
+                                         cmdline.inputFileCt);
+    }
+}
+
+
+
+static void
+freeInFileList(const char ** const inputFileNm,
+               unsigned int  const inputFileCt) {
+
+    unsigned int i;
+
+    for (i = 0; i < inputFileCt; ++i)
+        pm_strfree(inputFileNm[i]);
+
+    free(inputFileNm);
 }
 
 
@@ -1237,6 +1389,8 @@ main(int           argc,
      const char ** argv) {
 
     struct CmdlineInfo cmdline;
+    const char ** inputFileNm;
+    unsigned int inputFileCt;
     struct pam * inpam;  /* malloc'ed array */
     struct pam outpam;
     unsigned int i;
@@ -1245,20 +1399,23 @@ main(int           argc,
 
     parseCommandLine(argc, argv, &cmdline);
 
-    MALLOCARRAY_NOFAIL(inpam, cmdline.fileCt);
+    createInFileList(cmdline, !!cmdline.verbose, &inputFileNm, &inputFileCt);
+
+    MALLOCARRAY_NOFAIL(inpam, inputFileCt);
 
-    for (i = 0; i < cmdline.fileCt; ++i) {
-        FILE * const ifP = pm_openr(cmdline.inputFileName[i]);
+    for (i = 0; i < inputFileCt; ++i) {
+        FILE *  ifP;
+        ifP = pm_openr(inputFileNm[i]);
         inpam[i].comment_p = NULL;  /* Don't want to see the comments */
         pnm_readpaminit(ifP, &inpam[i], PAM_STRUCT_SIZE(opacity_plane));
     }
 
-    computeOutputParms(cmdline.fileCt, cmdline.orientation, inpam,
+    computeOutputParms(inputFileCt, cmdline.orientation, inpam,
                        cmdline.verbose, &outpam);
 
     outpam.file = stdout;
 
-    for (i = 0; i < cmdline.fileCt; ++i)
+    for (i = 0; i < inputFileCt; ++i)
         pnm_setminallocationdepth(&inpam[i], outpam.depth);
 
     pnm_writepaminit(&outpam);
@@ -1266,12 +1423,12 @@ main(int           argc,
     if (outpam.format == RPBM_FORMAT) {
         switch (cmdline.orientation) {
         case LEFTRIGHT:
-            concatenateLeftRightPbm(&outpam, inpam, cmdline.fileCt,
+            concatenateLeftRightPbm(&outpam, inpam, inputFileCt,
                                     cmdline.justification,
                                     cmdline.padColorMethod);
             break;
         case TOPBOTTOM:
-            concatenateTopBottomPbm(&outpam, inpam, cmdline.fileCt,
+            concatenateTopBottomPbm(&outpam, inpam, inputFileCt,
                                     cmdline.justification,
                                     cmdline.padColorMethod);
             break;
@@ -1279,21 +1436,22 @@ main(int           argc,
     } else {
         switch (cmdline.orientation) {
         case LEFTRIGHT:
-            concatenateLeftRightGen(&outpam, inpam, cmdline.fileCt,
+            concatenateLeftRightGen(&outpam, inpam, inputFileCt,
                                     cmdline.justification,
                                     cmdline.padColorMethod);
             break;
         case TOPBOTTOM:
-            concatenateTopBottomGen(&outpam, inpam, cmdline.fileCt,
+            concatenateTopBottomGen(&outpam, inpam, inputFileCt,
                                     cmdline.justification,
                                     cmdline.padColorMethod);
             break;
         }
     }
-    for (i = 0; i < cmdline.fileCt; ++i)
+    for (i = 0; i < inputFileCt; ++i)
         pm_close(inpam[i].file);
-    free(cmdline.inputFileName);
     free(inpam);
+    freeInFileList(inputFileNm, inputFileCt);
+    freeCmdLine(cmdline);
     pm_close(stdout);
 
     return 0;
diff --git a/editor/pamhomography.c b/editor/pamhomography.c
index 59b59ed7..02c0fd72 100644
--- a/editor/pamhomography.c
+++ b/editor/pamhomography.c
@@ -49,25 +49,55 @@
 #define MIN4(A, B, C, D) MIN(MIN(A, B), MIN(C, D))
 #define MAX4(A, B, C, D) MAX(MAX(A, B), MAX(C, D))
 
-/* A point on the image plane.  It may or may not lie within the
-   bounds of the image itself. */
-typedef struct Point {
+typedef struct {
+/*----------------------------------------------------------------------------
+  A point on the image plane.  It may or may not lie within the
+  bounds of the image itself.
+-----------------------------------------------------------------------------*/
     int x;
     int y;
 } Point;
 
-/* A quadrilateral on the image plane */
-typedef struct Quad {
+
+static Point
+pointXy(int const x,
+        int const y) {
+
+    Point retval;
+
+    retval.x = x;
+    retval.y = y;
+
+    return retval;
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  A quadrilateral on the image plane.
+-----------------------------------------------------------------------------*/
     Point ul;
     Point ur;
     Point lr;
     Point ll;
 } Quad;
 
-/* A user-specified mapping from one quadrilateral to another */
-typedef struct QuadMap {
-    Quad from;
-    Quad to;
+typedef struct {
+/*----------------------------------------------------------------------------
+  A specification of a quadrilateral on the image plane, either explicitly
+  or just as "the whole image".
+-----------------------------------------------------------------------------*/
+    bool wholeImage;
+    Quad explicit;  /* Meaningful only if 'wholeImage' is false */
+} QuadSpec;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   Specification of a mapping from one quadrilateral to another
+-----------------------------------------------------------------------------*/
+    QuadSpec from;
+    QuadSpec to;
 } QuadMap;
 
 struct CmdlineInfo {
@@ -75,21 +105,27 @@ struct CmdlineInfo {
        in a form easy for the program to use.
     */
     const char * inputFilespec;  /* "-" if stdin */
-    QuadMap      qmap;           /* Source and target quadrilaterals */
-    Quad         bbox;           /* Bounding box for the target image */
-    const char * fillColor;      /* Fill color for unused coordinates */
+    QuadMap      qmap;
+        /* Source and target quadrilaterals as specified by -to and -from;
+           Note that the file identified by 'mapfile' also supplies such
+           information.
+        */
+    QuadSpec     view;
+        /* Bounding box for the target image */
+    const char * mapfile;        /* Null if not specified */
+    const char * fill;           /* Null if not specified */
+    unsigned int verbose;
 };
 
 
 
-static unsigned int
-parseCoords(const char * const str,
-            int *        const coords) {
+static void
+parseCoords(const char *   const str,
+            int *          const coords,
+            unsigned int * const nP) {
 /*----------------------------------------------------------------------------
-  Parse a list of up to 16 integers.  The function returns the number
-  of integers encountered.
+  Parse a list of up to 16 integers.  Return as *nP how may there are.
 -----------------------------------------------------------------------------*/
-
     const char * p;
     char * pnext;
     unsigned int i;
@@ -106,138 +142,86 @@ parseCoords(const char * const str,
         errno = 0;  /* strtol() sets errno on error. */
         val = strtol(p, &pnext, 10);
         if (errno == ERANGE)
-            return i;  /* Integer lies out of long int range */
+            break;  /* Integer lies out of long int range */
         if (errno != 0 || pnext == p)
-            return i;  /* Too few integers */
+            break;  /* Too few integers */
         coords[i] = (int)val;
         if ((long int)coords[i] != val)
-            return i;  /* Integer lies out of int range */
+            break;  /* Integer lies out of int range */
     }
-    return i;
+    *nP = i;
 }
 
 
 
-static void
-parseViewString(const char * const str,
-                Quad *       const quad) {
+static Quad
+quadFmViewString(const char * const str) {
 /*----------------------------------------------------------------------------
   Parse a list of four integers in the order {ulx, uly, lrx, lry} into a
-  quadrilateral.  The function aborts on error.
------------------------------------------------------------------------------*/
+  quadrilateral.
 
+  Abort the program if 'str' is not valid.
+-----------------------------------------------------------------------------*/
+    Quad retval;
     int coords[16];
+    unsigned int nCoord;
+
+    parseCoords(str, coords, &nCoord);
+
+    if (nCoord != 4)
+        pm_error("failed to parse '%s' as a list of four integers", str);
 
-    if (parseCoords(str, coords) != 4)
-        pm_error("failed to parse \"%s\" as a list of four integers", str);
-    quad->ul.x = quad->ll.x = coords[0];
-    quad->ul.y = quad->ur.y = coords[1];
-    quad->lr.x = quad->ur.x = coords[2];
-    quad->lr.y = quad->ll.y = coords[3];
+    retval.ul.x = retval.ll.x = coords[0];
+    retval.ul.y = retval.ur.y = coords[1];
+    retval.lr.x = retval.ur.x = coords[2];
+    retval.lr.y = retval.ll.y = coords[3];
+
+    return retval;
 }
 
 
 
-static void
-parseQuadString(const char * const str,
-                Quad *       const quad) {
-/*----------------------------------------------------------------------------
-  Parse a list of eight integers in the order {ulx, uly, urx, ury,
-  lrx, lry, llx, lly} into a quadrilateral.  The function aborts on
-  error.
------------------------------------------------------------------------------*/
+static Quad
+quadFmIntList(const int * const coords) {
+    Quad retval;
 
-    int coords[16];
+    retval.ul.x = coords[0];
+    retval.ul.y = coords[1];
+    retval.ur.x = coords[2];
+    retval.ur.y = coords[3];
+    retval.lr.x = coords[4];
+    retval.lr.y = coords[5];
+    retval.ll.x = coords[6];
+    retval.ll.y = coords[7];
 
-    if (parseCoords(str, coords) != 8)
-        pm_error("failed to parse \"%s\" as a list of eight integers", str);
-    quad->ul.x = coords[0];
-    quad->ul.y = coords[1];
-    quad->ur.x = coords[2];
-    quad->ur.y = coords[3];
-    quad->lr.x = coords[4];
-    quad->lr.y = coords[5];
-    quad->ll.x = coords[6];
-    quad->ll.y = coords[7];
+    return retval;
 }
 
 
 
-static void
-readMapFile(const char * const fname,
-            QuadMap *    const qmap) {
+static Quad
+quadFmString(const char * const str) {
 /*----------------------------------------------------------------------------
-  Read from a file either 16 numbers in the order {ulx1, uly1, urx1, ury1,
-  lrx1, lry1, llx1, lly1, ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, lly2}
-  or 8 numbers in the order {ulx2, uly2, urx2, ury2, lrx2, lry2, llx2,
-  lly2}.  This function aborts on error.
------------------------------------------------------------------------------*/
-
-    FILE * fp;
-    char * str;      /* Entire file contents */
-    int coords[16];  /* File as a list of up to 16 coordinates */
-    char * c;
-    long int nread;
+  Parse a list of eight integers in the order {ulx, uly, urx, ury,
+  lrx, lry, llx, lly} into a quadrilateral.
 
-    /* Read the entire file. */
-    fp = pm_openr(fname);
-    str = pm_read_unknown_size(fp, &nread);
-    REALLOCARRAY_NOFAIL(str, nread + 1);
-    str[nread] = '\0';
-    pm_close(fp);
+  Abort the program if 'str' is not a valid list of eight integers.
+-----------------------------------------------------------------------------*/
+    int coords[16];
+    unsigned int nCoord;
 
-    /* Replace newlines and tabs with spaces to prettify error reporting. */
-    for (c = str; *c != '\0'; ++c)
-        if (isspace(*c))
-            *c = ' ';
+    parseCoords(str, coords, &nCoord);
 
-    /* Read either {from, to} or just a {to} quadrilateral. */
-    switch (parseCoords(str, coords)) {
-    case 16:
-        /* 16 integers: assign both the "from" and the "to" quadrilateral. */
-        qmap->from.ul.x = coords[0];
-        qmap->from.ul.y = coords[1];
-        qmap->from.ur.x = coords[2];
-        qmap->from.ur.y = coords[3];
-        qmap->from.lr.x = coords[4];
-        qmap->from.lr.y = coords[5];
-        qmap->from.ll.x = coords[6];
-        qmap->from.ll.y = coords[7];
-        qmap->to.ul.x = coords[8];
-        qmap->to.ul.y = coords[9];
-        qmap->to.ur.x = coords[10];
-        qmap->to.ur.y = coords[11];
-        qmap->to.lr.x = coords[12];
-        qmap->to.lr.y = coords[13];
-        qmap->to.ll.x = coords[14];
-        qmap->to.ll.y = coords[15];
-        break;
-    case 8:
-        /* 8 integers: assign only the "to" quadrilateral. */
-        memset((void *)&qmap->from, 0, sizeof(Quad));
-        qmap->to.ul.x = coords[0];
-        qmap->to.ul.y = coords[1];
-        qmap->to.ur.x = coords[2];
-        qmap->to.ur.y = coords[3];
-        qmap->to.lr.x = coords[4];
-        qmap->to.lr.y = coords[5];
-        qmap->to.ll.x = coords[6];
-        qmap->to.ll.y = coords[7];
-        break;
-    default:
-        /* Any other number of integers: issue an error message. */
-        pm_error("failed to parse \"%s\" as a list of either 8 or 16 integers",
-                 str);
-        break;
-    }
+    if (nCoord != 8)
+        pm_error("failed to parse '%s' as a list of eight integers", str);
 
-    free(str);
+    return quadFmIntList(coords);
 }
 
 
 
 static void
-parseCommandLine(int                  argc,
+parseCommandLine(int                        argc,
                  const char **        const argv,
                  struct CmdlineInfo * const cmdlineP ) {
 /*----------------------------------------------------------------------------
@@ -255,23 +239,26 @@ parseCommandLine(int                  argc,
 
     unsigned int option_def_index;
 
-    unsigned int mapFileSpec = 0, fromSpec = 0, toSpec = 0;
-    unsigned int viewSpec = 0, fillColorSpec = 0;
-    char *mapFile, *from, *to, *view;
+    unsigned int mapfileSpec, fromSpec, toSpec, viewSpec, fillSpec;
+    const char * from;
+    const char * to;
+    const char * view;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "mapfile",         OPT_STRING, &mapFile,
-            &mapFileSpec,             0);
+    OPTENT3(0, "mapfile",         OPT_STRING, &cmdlineP->mapfile,
+            &mapfileSpec,             0);
     OPTENT3(0, "from",            OPT_STRING, &from,
             &fromSpec,                0);
     OPTENT3(0, "to",              OPT_STRING, &to,
             &toSpec,                  0);
     OPTENT3(0, "view",            OPT_STRING, &view,
             &viewSpec,                0);
-    OPTENT3(0, "fill",            OPT_STRING, &cmdlineP->fillColor,
-            &fillColorSpec,           0);
+    OPTENT3(0, "fill",            OPT_STRING, &cmdlineP->fill,
+            &fillSpec,                0);
+    OPTENT3(0, "verbose",         OPT_FLAG,   NULL,
+            &cmdlineP->verbose,       0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -280,23 +267,29 @@ parseCommandLine(int                  argc,
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and local variables. */
 
-    if (!fillColorSpec)
-        cmdlineP->fillColor = NULL;
-
-    memset((void *)&cmdlineP->qmap, 0, sizeof(QuadMap));
-    if (mapFileSpec)
-        readMapFile(mapFile, &cmdlineP->qmap);
-    if (fromSpec)
-        parseQuadString(from, &cmdlineP->qmap.from);
-    if (toSpec)
-        parseQuadString(to, &cmdlineP->qmap.to);
-    if (!mapFileSpec && !fromSpec && !toSpec && !viewSpec)
-        pm_error("You must specify at least one of "
-                 "-mapfile, -qin, -qout, and -view");
-    if (viewSpec)
-        parseViewString(view, &cmdlineP->bbox);
-    else
-        memset((void *)&cmdlineP->bbox, 0, sizeof(Quad));
+    if (!fillSpec)
+        cmdlineP->fill = NULL;
+
+    if (!mapfileSpec)
+        cmdlineP->mapfile = NULL;
+
+    if (fromSpec) {
+        cmdlineP->qmap.from.wholeImage = false;
+        cmdlineP->qmap.from.explicit = quadFmString(from);
+    } else
+        cmdlineP->qmap.from.wholeImage = true;
+
+    if (toSpec) {
+        cmdlineP->qmap.to.wholeImage = false;
+        cmdlineP->qmap.to.explicit = quadFmString(to);
+    } else
+        cmdlineP->qmap.to.wholeImage = true;
+
+    if (viewSpec) {
+        cmdlineP->view.wholeImage = false;
+        cmdlineP->view.explicit   = quadFmViewString(view);
+    } else
+        cmdlineP->view.wholeImage = true;
 
     if (argc < 2)
         cmdlineP->inputFilespec = "-";
@@ -304,77 +297,168 @@ parseCommandLine(int                  argc,
         cmdlineP->inputFilespec = argv[1];
     else
         pm_error("Too many non-option arguments: %u.  "
-                 "Only argument is input file name", argc - 1);
+                 "Only possible argument is input file name", argc - 1);
 
     free((void *) option_def);
 }
 
 
 
-static tuple
-parseFillColor(const struct pam *         const pamP,
-               const struct CmdlineInfo * const cmdlineP) {
+static void
+readMapFile(const char * const fname,
+            QuadMap *    const qmapP) {
 /*----------------------------------------------------------------------------
-  Parse the fill color into the correct format for the given PAM metadata.
+  Read from a file either 16 numbers in the order {ulx1, uly1, urx1, ury1,
+  lrx1, lry1, llx1, lly1, ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, lly2}
+  or 8 numbers in the order {ulx2, uly2, urx2, ury2, lrx2, lry2, llx2,
+  lly2}.
+
+  Abort the program if the file does not contain data in this format.
 -----------------------------------------------------------------------------*/
+    FILE * fp;
+    char * str;      /* Entire file contents */
+    int coords[16];  /* File as a list of up to 16 coordinates */
+    unsigned int nCoord;
+    long int nread;
 
-    tuple rgb;
-    tuple fillColor;
+    /* Read the entire file. */
+    fp = pm_openr(fname);
+    str = pm_read_unknown_size(fp, &nread);
+    REALLOCARRAY_NOFAIL(str, nread + 1);
+    str[nread] = '\0';
+    pm_close(fp);
 
-    if (!cmdlineP->fillColor) {
-        pnm_createBlackTuple(pamP, &fillColor);
-        return fillColor;
+    {
+        unsigned int i;
+        /* Replace newlines and tabs with spaces to prettify error
+           reporting.
+        */
+        for (i = 0; str[i]; ++i)
+            if (isspace(str[i]))
+                str[i] = ' ';
     }
+    parseCoords(str, coords, &nCoord);
 
-    rgb = pnm_parsecolor(cmdlineP->fillColor, pamP->maxval);
-    fillColor = pnm_allocpamtuple(pamP);
-    switch (pamP->depth) {
-    case 1:
-        /* Grayscale */
-        fillColor[0] = (rgb[PAM_RED_PLANE]*299 +
-                        rgb[PAM_GRN_PLANE]*587 +
-                        rgb[PAM_BLU_PLANE]*114)/1000;
-        break;
-    case 2:
-        /* Grayscale + alpha */
-        fillColor[0] = (rgb[PAM_RED_PLANE]*299 +
-                        rgb[PAM_GRN_PLANE]*587 +
-                        rgb[PAM_BLU_PLANE]*114)/1000;
-        fillColor[PAM_GRAY_TRN_PLANE] = pamP->maxval;
-        break;
-    case 3:
-        /* RGB */
-        pnm_assigntuple(pamP, fillColor, rgb);
+    /* Read either {from, to} or just a {to} quadrilateral. */
+    switch (nCoord) {
+    case 16:
+        /* 16 integers: assign both the "from" and the "to" quadrilateral. */
+        qmapP->from.wholeImage = false;
+        qmapP->from.explicit   = quadFmIntList(&coords[0]);
+        qmapP->to.wholeImage   = false;
+        qmapP->to.explicit     = quadFmIntList(&coords[8]);
         break;
-    case 4:
-        /* RGB + alpha */
-        pnm_assigntuple(pamP, fillColor, rgb);
-        fillColor[PAM_TRN_PLANE] = pamP->maxval;
+    case 8:
+        /* 8 integers: assign only the "to" quadrilateral. */
+        qmapP->from.wholeImage = true;
+        qmapP->to.explicit     = quadFmIntList(coords);
         break;
     default:
-        pm_error("unexpected image depth %d", pamP->depth);
+        /* Not valid input */
+        pm_error("failed to parse contents of map file '%s' ('%s') "
+                 "as a list of either 8 or 16 integers",
+                 fname, str);
         break;
     }
 
-    return fillColor;
+    free(str);
 }
 
 
 
-static tuple **
-initOutputImage(const struct pam *         const pamP,
-                const struct CmdlineInfo * const cmdlineP) {
+static void
+reportQuads(Quad const qfrom,
+            Quad const qto) {
+
+    pm_message("Copying from ((%d,%d),(%d,%d),(%d,%d),(%d,%d)) "
+               "to ((%d,%d),(%d,%d),(%d,%d),(%d,%d))",
+               qfrom.ul.x, qfrom.ul.y,
+               qfrom.ur.x, qfrom.ur.y,
+               qfrom.lr.x, qfrom.lr.y,
+               qfrom.ll.x, qfrom.ll.y,
+               qto.ul.x,   qto.ul.y,
+               qto.ur.x,   qto.ur.y,
+               qto.lr.x,   qto.lr.y,
+               qto.ll.x,   qto.ll.y
+        );
+}
+
+
+
+static void
+reportBbox(Quad const bbox) {
+
+    pm_message("The bounding box is ((%d,%d),(%d,%d),(%d,%d),(%d,%d))",
+               bbox.ul.x, bbox.ul.y,
+               bbox.ur.x, bbox.ur.y,
+               bbox.lr.x, bbox.lr.y,
+               bbox.ll.x, bbox.ll.y
+        );
+}
+
+
+
+static tuple
+parseFillColor(const struct pam * const pamP,
+               const char *       const fillColorSpec) {
 /*----------------------------------------------------------------------------
-  Allocate and initialize the output image.
+  Parse the fill color into the correct format for the given PAM metadata.
 -----------------------------------------------------------------------------*/
+    tuple retval;
+
+    if (!fillColorSpec)
+        pnm_createBlackTuple(pamP, &retval);
+    else {
+        tuple const rgb = pnm_parsecolor(fillColorSpec, pamP->maxval);
+
+        retval = pnm_allocpamtuple(pamP);
+
+        switch (pamP->depth) {
+        case 1:
+            /* Grayscale */
+            retval[0] = (rgb[PAM_RED_PLANE]*299 +
+                         rgb[PAM_GRN_PLANE]*587 +
+                         rgb[PAM_BLU_PLANE]*114)/1000;
+            break;
+        case 2:
+            /* Grayscale + alpha */
+            retval[0] = (rgb[PAM_RED_PLANE]*299 +
+                         rgb[PAM_GRN_PLANE]*587 +
+                         rgb[PAM_BLU_PLANE]*114)/1000;
+            retval[PAM_GRAY_TRN_PLANE] = pamP->maxval;
+            break;
+        case 3:
+            /* RGB */
+            pnm_assigntuple(pamP, retval, rgb);
+            break;
+        case 4:
+            /* RGB + alpha */
+            pnm_assigntuple(pamP, retval, rgb);
+            retval[PAM_TRN_PLANE] = pamP->maxval;
+            break;
+        default:
+            pm_error("unexpected image depth %d", pamP->depth);
+            break;
+        }
+    }
+    return retval;
+}
 
+
+
+static tuple **
+initOutputImage(const struct pam * const pamP,
+                const char *       const fillColorSpec) {
+/*----------------------------------------------------------------------------
+  Allocate and initialize the output image data structure.
+-----------------------------------------------------------------------------*/
     tuple fillColor;    /* Fill color to use for unused coordinates */
     tuple ** outImg;    /* Output image */
     unsigned int row;
 
     outImg = pnm_allocpamarray(pamP);
 
-    fillColor = parseFillColor(pamP, cmdlineP);
+    fillColor = parseFillColor(pamP, fillColorSpec);
     for (row = 0; row < pamP->height; ++row) {
         unsigned int col;
 
@@ -390,138 +474,142 @@ initOutputImage(const struct pam *         const pamP,
 
 
 static void
-computeSteps(const Quad * const qfrom,
-             const Quad * const qto,
-             double *     const ustep,
-             double *     const vstep) {
+computeSteps(Quad     const qfrom,
+             Quad     const qto,
+             double * const ustepP,
+             double * const vstepP) {
 /*----------------------------------------------------------------------------
   Compute increments for u and v as these range from 0.0 to 1.0.
 -----------------------------------------------------------------------------*/
-
     double fx0, fx1, fxd;
     double tx0, tx1, txd;
     double fy0, fy1, fyd;
     double ty0, ty1, tyd;
 
     /* Compute ustep as the inverse of the maximum possible x delta across
-       either the "from" or "to" quadrilateral. */
-    fx0 = MIN4((double)qfrom->ur.x,
-               (double)qfrom->ul.x,
-               (double)qfrom->lr.x,
-               (double)qfrom->ll.x);
-    fx1 = MAX4((double)qfrom->ur.x,
-               (double)qfrom->ul.x,
-               (double)qfrom->lr.x,
-               (double)qfrom->ll.x);
+       either the "from" or "to" quadrilateral.
+    */
+    fx0 = MIN4((double)qfrom.ur.x,
+               (double)qfrom.ul.x,
+               (double)qfrom.lr.x,
+               (double)qfrom.ll.x);
+    fx1 = MAX4((double)qfrom.ur.x,
+               (double)qfrom.ul.x,
+               (double)qfrom.lr.x,
+               (double)qfrom.ll.x);
     fxd = fx1 - fx0;
-    tx0 = MIN4((double)qto->ur.x,
-               (double)qto->ul.x,
-               (double)qto->lr.x,
-               (double)qto->ll.x);
-    tx1 = MAX4((double)qto->ur.x,
-               (double)qto->ul.x,
-               (double)qto->lr.x,
-               (double)qto->ll.x);
+
+    tx0 = MIN4((double)qto.ur.x,
+               (double)qto.ul.x,
+               (double)qto.lr.x,
+               (double)qto.ll.x);
+    tx1 = MAX4((double)qto.ur.x,
+               (double)qto.ul.x,
+               (double)qto.lr.x,
+               (double)qto.ll.x);
     txd = tx1 - tx0;
+
     if (fxd == 0.0 && txd == 0.0)
-        *ustep = 1.0;  /* Arbitrary nonzero step */
-    *ustep = 0.5/MAX(fxd, txd);
-        /* Divide into 0.5 instead of 1.0 for additional smoothing. */
+        *ustepP = 1.0;  /* Arbitrary nonzero step */
+    else
+        *ustepP = 0.5/MAX(fxd, txd);
+            /* Divide into 0.5 instead of 1.0 for additional smoothing. */
 
     /* Compute vstep as the inverse of the maximum possible y delta across
-       either the "from" or "to" quadrilateral
-  . */
-    fy0 = MIN4((double)qfrom->ur.y,
-               (double)qfrom->ul.y,
-               (double)qfrom->lr.y,
-               (double)qfrom->ll.y);
-    fy1 = MAX4((double)qfrom->ur.y,
-               (double)qfrom->ul.y,
-               (double)qfrom->lr.y,
-               (double)qfrom->ll.y);
+       either the "from" or "to" quadrilateral.
+    */
+    fy0 = MIN4((double)qfrom.ur.y,
+               (double)qfrom.ul.y,
+               (double)qfrom.lr.y,
+               (double)qfrom.ll.y);
+    fy1 = MAX4((double)qfrom.ur.y,
+               (double)qfrom.ul.y,
+               (double)qfrom.lr.y,
+               (double)qfrom.ll.y);
     fyd = fy1 - fy0;
-    ty0 = MIN4((double)qto->ur.y,
-               (double)qto->ul.y,
-               (double)qto->lr.y,
-               (double)qto->ll.y);
-    ty1 = MAX4((double)qto->ur.y,
-               (double)qto->ul.y,
-               (double)qto->lr.y,
-               (double)qto->ll.y);
+    ty0 = MIN4((double)qto.ur.y,
+               (double)qto.ul.y,
+               (double)qto.lr.y,
+               (double)qto.ll.y);
+    ty1 = MAX4((double)qto.ur.y,
+               (double)qto.ul.y,
+               (double)qto.lr.y,
+               (double)qto.ll.y);
     tyd = ty1 - ty0;
+
     if (fyd == 0.0 && tyd == 0.0)
-        *vstep = 1.0;  /* Arbitrary nonzero step */
-    *vstep = 0.5/MAX(fyd, tyd);
-        /* Divide into 0.5 instead of 1.0 for additional smoothing. */
+        *vstepP = 1.0;  /* Arbitrary nonzero step */
+    else
+        *vstepP = 0.5/MAX(fyd, tyd);
+            /* Divide into 0.5 instead of 1.0 for additional smoothing. */
 }
 
 
 
-static Quad *
-prepareQuadrilateral(const struct pam * const pamP,
-                     const Quad *       const qdata) {
+static Quad
+quadrilateralFmSpec(const struct pam * const pamP,
+                    QuadSpec           const qdata) {
 /*----------------------------------------------------------------------------
-  If a quadrilateral has all zero points, replace it with a quadrilateral
-  of the full size of the image.  The caller should free the result.
+  The quadrilateral specified by 'qdata'.
 -----------------------------------------------------------------------------*/
+    Quad retval;
 
-    Quad * qcopy;
-
-    MALLOCVAR_NOFAIL(qcopy);
-
-    if (qdata->ul.x == 0 && qdata->ul.y == 0 &&
-        qdata->ur.x == 0 && qdata->ur.y == 0 &&
-        qdata->ll.x == 0 && qdata->ll.y == 0 &&
-        qdata->lr.x == 0 && qdata->lr.y == 0) {
+    if (qdata.wholeImage) {
         /* Set the quadrilateral to the image's bounding box. */
-        memset((void *)qcopy, 0, sizeof(Quad));
-        qcopy->ur.x = pamP->width - 1;
-        qcopy->lr.x = pamP->width - 1;
-        qcopy->lr.y = pamP->height - 1;
-        qcopy->ll.y = pamP->height - 1;
+        retval.ul = pointXy(0,               0               );
+        retval.ur = pointXy(pamP->width - 1, 0               );
+        retval.ll = pointXy(0,               pamP->height - 1);
+        retval.lr = pointXy(pamP->width - 1, pamP->height - 1);
     } else {
         /* Use the quadrilateral as specified. */
-        memcpy(qcopy, qdata, sizeof(Quad));
+        retval = qdata.explicit;
     }
 
-    return qcopy;
+    return retval;
 }
 
 
-static void
-coordsAtPercent(const Quad * const quad,
-                double       const u,
-                double       const v,
-                int *        const x,
-                int *        const y) {
+
+static Point
+coordsAtPercent(Quad   const quad,
+                double const u,
+                double const v) {
 /*----------------------------------------------------------------------------
   Return the (x, y) coordinates that lie at (u%, v%) from the upper left to
   the lower right of a given quadrilateral.
 -----------------------------------------------------------------------------*/
-
-    *x = (int) nearbyint((1.0 - u)*(1.0 - v)*quad->ul.x +
-                         u*(1.0 - v)*quad->ur.x +
-                         u*v*quad->lr.x +
-                         (1.0 - u)*v*quad->ll.x);
-    *y = (int) nearbyint((1.0 - u)*(1.0 - v)*quad->ul.y +
-                         u*(1.0 - v)*quad->ur.y +
-                         u*v*quad->lr.y +
-                         (1.0 - u)*v*quad->ll.y);
+    return pointXy(
+        (int) nearbyint((1.0 - u) * (1.0 - v) * quad.ul.x +   /* x */
+                        u * (1.0 - v) * quad.ur.x +
+                        u * v * quad.lr.x +
+                        (1.0 - u) * v * quad.ll.x),
+        (int) nearbyint((1.0 - u) * (1.0 - v) * quad.ul.y +   /* y */
+                        u * (1.0 - v) * quad.ur.y +
+                        u * v * quad.lr.y +
+                        (1.0 - u) * v * quad.ll.y)
+        );
 }
 
 
 
-static void
-computeBoundingBox(const Quad * const q,
-                   Quad *       const bbox) {
+static Quad
+boundingBoxOfQuadrilateral(Quad const q) {
 /*----------------------------------------------------------------------------
-  Compute the bounding box of a given quadrilateral.
+  The bounding box of quadrilateral 'q'.
 -----------------------------------------------------------------------------*/
+    Quad retval;
 
-    bbox->ul.x = bbox->ll.x = MIN4(q->ul.x, q->ur.x, q->lr.x, q->ll.x);
-    bbox->ul.y = bbox->ur.y = MIN4(q->ul.y, q->ur.y, q->lr.y, q->ll.y);
-    bbox->ur.x = bbox->lr.x = MAX4(q->ul.x, q->ur.x, q->lr.x, q->ll.x);
-    bbox->ll.y = bbox->lr.y = MAX4(q->ul.y, q->ur.y, q->lr.y, q->ll.y);
+    int const leftLimit = MIN4(q.ul.x, q.ur.x, q.lr.x, q.ll.x);
+    int const rghtLimit = MAX4(q.ul.x, q.ur.x, q.lr.x, q.ll.x);
+    int const topLimit  = MIN4(q.ul.y, q.ur.y, q.lr.y, q.ll.y);
+    int const botLimit  = MAX4(q.ul.y, q.ur.y, q.lr.y, q.ll.y);
+
+    retval.ul = pointXy(leftLimit, topLimit);
+    retval.ur = pointXy(rghtLimit, topLimit);
+    retval.ll = pointXy(leftLimit, botLimit);
+    retval.lr = pointXy(rghtLimit, botLimit);
+
+    return retval;
 }
 
 
@@ -529,8 +617,8 @@ computeBoundingBox(const Quad * const q,
 static void
 mapQuadrilaterals(const struct pam * const inPamP,
                   const struct pam * const outPamP,
-                  const Quad *       const qfrom,
-                  const Quad *       const qto,
+                  Quad               const qfrom,
+                  Quad               const qto,
                   tuple **           const inImg,
                   tuple **           const outImg,
                   int                const xofs,
@@ -540,109 +628,120 @@ mapQuadrilaterals(const struct pam * const inPamP,
   target image.  This is the function that implemens pamhomography's
   primary functionality.
 -----------------------------------------------------------------------------*/
-
     sample ** channel;
         /* Aggregated values for a single channel */
     unsigned long ** tally;
         /* Number of values at each coordinate in the above */
     double ustep, vstep;
         /* Steps to use when iterating from 0.0 to 1.0 */
-    double u, v;
-    unsigned int plane, row, col;
+    unsigned int plane;
 
     MALLOCARRAY2_NOFAIL(channel, outPamP->height, outPamP->width);
-    MALLOCARRAY2_NOFAIL(tally, outPamP->height, outPamP->width);
+    MALLOCARRAY2_NOFAIL(tally,   outPamP->height, outPamP->width);
 
     computeSteps(qfrom, qto, &ustep, &vstep);
 
     for (plane = 0; plane < outPamP->depth; ++plane) {
         /* Reset the channel colors and tally for each plane, */
-        for (row = 0; row < outPamP->height; ++row)
+        unsigned int row;
+        double v;
+        for (row = 0; row < outPamP->height; ++row) {
+            unsigned int col;
             for (col = 0; col < outPamP->width; ++col) {
                 channel[row][col] = 0;
-                tally[row][col] = 0;
+                tally  [row][col] = 0;
             }
-
+        }
         /* Iterate from 0% to 100% in the y dimension. */
         for (v = 0.0; v <= 1.0; v += vstep) {
             /* Iterate from 0% to 100% in the x dimension. */
+            double u;
             for (u = 0.0; u <= 1.0; u += ustep) {
-                int x0, y0;  /* "From" coordinate */
-                int x1, y1;  /* "To" coordinate */
-
-                /* Map (u%, v%) of one quadrilateral to (u%, v%) of the
-                   other quadrilateral. */
-                coordsAtPercent(qfrom, u, v, &x0, &y0);
-                coordsAtPercent(qto, u, v, &x1, &y1);
-
-                /* Copy the source image's (x0, y0) to the destination
-                   image's (x1, y1) in the current plane. */
-                x1 += xofs;
-                y1 += yofs;
-                if (x0 >= 0 && y0 >= 0 &&
-                    x0 < inPamP->width && y0 < inPamP->height &&
-                    x1 >= 0 && y1 >= 0 &&
-                    x1 < outPamP->width && y1 < outPamP->height) {
-                    channel[y1][x1] += inImg[y0][x0][plane];
-                    tally[y1][x1]++;
+                Point from;  /* "From" coordinate */
+                Point to;    /* "To" coordinate */
+
+                /* Map (u%, v%) of one quadrilateral to (u%, v%) of the other
+                   quadrilateral.
+                */
+                from = coordsAtPercent(qfrom, u, v);
+                to   = coordsAtPercent(qto,   u, v);
+
+                /* Copy the source image's 'from' pixel as the destination
+                   image's 'to' pixel in the current plane.
+                */
+                to.x += xofs;
+                to.y += yofs;
+                if (from.x >= 0 && from.y >= 0 &&
+                    from.x < inPamP->width && from.y < inPamP->height &&
+                    to.x >= 0 && to.y >= 0 &&
+                    to.x < outPamP->width && to.y < outPamP->height) {
+
+                    channel[to.y][to.x] += inImg[from.y][from.x][plane];
+                    ++tally[to.y][to.x];
                 }
             }
         }
 
         /* Assign the current plane in the output image the average color
            at each point. */
-        for (row = 0; row < outPamP->height; ++row)
-            for (col = 0; col < outPamP->width; ++col)
+        for (row = 0; row < outPamP->height; ++row) {
+            unsigned int col;
+            for (col = 0; col < outPamP->width; ++col) {
                 if (tally[row][col] != 0)
                     outImg[row][col][plane] =
                         (channel[row][col] + tally[row][col]/2) /
                         tally[row][col];
+            }
+        }
     }
 
     pm_freearray2((void ** const)tally);
     pm_freearray2((void ** const)channel);
-    free((void *)qto);
-    free((void *)qfrom);
 }
 
 
 
 static void
-processFile(FILE *                     const ifP,
-            const struct CmdlineInfo * const cmdlineP) {
+processFile(FILE *       const ifP,
+            QuadMap      const qmap,
+            QuadSpec     const view,
+            const char * const fillColorSpec,
+            bool         const verbose) {
 /*----------------------------------------------------------------------------
   Read the input image, create the output image, and map a quadrilateral in
   the former to a quadrilateral in the latter.
 -----------------------------------------------------------------------------*/
-
     struct pam inPam;    /* PAM metadata for the input file */
     struct pam outPam;   /* PAM metadata for the output file */
     tuple ** inImg;      /* Input image */
     tuple ** outImg;     /* Output image */
-    Quad *qfrom, *qto;   /* Source and target quadrilaterals */
+    Quad qfrom, qto;     /* Source and target quadrilaterals */
     Quad bbox;           /* Bounding box around the transformed input image */
 
     inImg = pnm_readpam(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type));
 
     /* Extract quadrilaterals and populate them with the image bounds
        if necessary. */
-    qfrom = prepareQuadrilateral(&inPam, &cmdlineP->qmap.from);
-    qto = prepareQuadrilateral(&inPam, &cmdlineP->qmap.to);
+    qfrom = quadrilateralFmSpec(&inPam, qmap.from);
+    qto   = quadrilateralFmSpec(&inPam, qmap.to);
+
+    if (verbose)
+        reportQuads(qfrom, qto);
 
     /* Allocate storage for the target image. */
-    if (cmdlineP->bbox.ul.x == 0 && cmdlineP->bbox.ul.y == 0 &&
-        cmdlineP->bbox.lr.x == 0 && cmdlineP->bbox.lr.y == 0)
-        /* User did not specify a target bounding box.  Compute optimal
-           dimensions. */
-        computeBoundingBox(qto, &bbox);
+    if (view.wholeImage)
+        bbox = boundingBoxOfQuadrilateral(qto);
     else
-        /* User specified a target bounding box.  Use it. */
-        bbox = cmdlineP->bbox;
-    outPam = inPam;
-    outPam.file = stdout;
-    outPam.width = bbox.lr.x - bbox.ul.x + 1;
+        bbox = view.explicit;
+
+    if (verbose)
+        reportBbox(bbox);
+
+    outPam = inPam;  /* initial value */
+    outPam.file   = stdout;
+    outPam.width  = bbox.lr.x - bbox.ul.x + 1;
     outPam.height = bbox.lr.y - bbox.ul.y + 1;
-    outImg = initOutputImage(&outPam, cmdlineP);
+    outImg        = initOutputImage(&outPam, fillColorSpec);
 
     mapQuadrilaterals(&inPam, &outPam,
                       qfrom, qto,
@@ -662,16 +761,39 @@ main(int argc, const char *argv[]) {
 
     struct CmdlineInfo cmdline;      /* Parsed command line */
     FILE * ifP;
+    QuadMap qmap;
 
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
+    if (cmdline.mapfile) {
+        /* Use the from and/or to values from the map file where the user
+           didn't explicitly state them
+        */
+        QuadMap mapFileValue;
+
+        readMapFile(cmdline.mapfile, &mapFileValue);
+
+        if (cmdline.qmap.from.wholeImage)
+            qmap.from = mapFileValue.from;
+        else
+            qmap.from = cmdline.qmap.from;
+
+        if (cmdline.qmap.to.wholeImage)
+            qmap.to = mapFileValue.to;
+        else
+            qmap.to = cmdline.qmap.to;
+    } else
+        qmap = cmdline.qmap;
+
     ifP = pm_openr(cmdline.inputFilespec);
 
-    processFile(ifP, &cmdline);
+    processFile(ifP, qmap, cmdline.view, cmdline.fill, !!cmdline.verbose);
 
     pm_close(ifP);
 
     return 0;
 }
+
+
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index 7da3122b..f10cc15c 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -1,7 +1,6 @@
-/******************************************************************************
-                               pnmcolormap.c
-*******************************************************************************
-
+/*=============================================================================
+                               pnmcolormap
+===============================================================================
   Create a colormap file (a PPM image containing one pixel of each of a set
   of colors).  Base the set of colors on an input image.
 
@@ -20,9 +19,7 @@
   copyright notice and this permission notice appear in supporting
   documentation.  This software is provided "as is" without express or
   implied warranty.
-
-******************************************************************************/
-
+=============================================================================*/
 #include <assert.h>
 #include <math.h>
 
@@ -308,7 +305,7 @@ sortBoxes(struct BoxVector *  const boxVectorP,
 */
 
 static void
-findBoxBoundaries(tupletable2  const colorfreqtable,
+findBoxBoundaries(tupletable2  const colorFreqTable,
                   unsigned int const depth,
                   unsigned int const boxStart,
                   unsigned int const boxSize,
@@ -322,14 +319,14 @@ findBoxBoundaries(tupletable2  const colorfreqtable,
     unsigned int i;
 
     for (plane = 0; plane < depth; ++plane) {
-        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
+        minval[plane] = colorFreqTable.table[boxStart]->tuple[plane];
         maxval[plane] = minval[plane];
     }
 
     for (i = 1; i < boxSize; ++i) {
         unsigned int plane;
         for (plane = 0; plane < depth; ++plane) {
-            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
+            sample const v = colorFreqTable.table[boxStart + i]->tuple[plane];
             if (v < minval[plane]) minval[plane] = v;
             if (v > maxval[plane]) maxval[plane] = v;
         }
@@ -402,7 +399,7 @@ findPlaneWithLargestSpreadByLuminosity(sample         const minval[],
 
 static void
 computeBoxSpread(const struct Box *    const boxP,
-                 tupletable2           const colorfreqtable,
+                 tupletable2           const colorFreqTable,
                  unsigned int          const depth,
                  enum MethodForLargest const methodForLargest,
                  unsigned int *        const planeWithLargestP,
@@ -420,7 +417,7 @@ computeBoxSpread(const struct Box *    const boxP,
     MALLOCARRAY_NOFAIL(minval, depth);
     MALLOCARRAY_NOFAIL(maxval, depth);
 
-    findBoxBoundaries(colorfreqtable, depth, boxP->index, boxP->colorCt,
+    findBoxBoundaries(colorFreqTable, depth, boxP->index, boxP->colorCt,
                       minval, maxval);
 
     switch (methodForLargest) {
@@ -439,13 +436,13 @@ computeBoxSpread(const struct Box *    const boxP,
 
 
 static unsigned int
-freqTotal(tupletable2 const freqtable) {
+freqTotal(tupletable2 const freqTable) {
 
     unsigned int i;
     unsigned int sum;
 
-    for (i = 0, sum = 0; i < freqtable.size; ++i)
-        sum += freqtable.table[i]->value;
+    for (i = 0, sum = 0; i < freqTable.size; ++i)
+        sum += freqTable.table[i]->value;
 
     return sum;
 }
@@ -453,13 +450,13 @@ freqTotal(tupletable2 const freqtable) {
 
 
 static struct BoxVector
-newBoxVector(tupletable2           const colorfreqtable,
+newBoxVector(tupletable2           const colorFreqTable,
              unsigned int          const capacity,
              unsigned int          const depth,
              enum MethodForLargest const methodForLargest) {
 
-    unsigned int const colorCt = colorfreqtable.size;
-    unsigned int const sum     = freqTotal(colorfreqtable);
+    unsigned int const colorCt = colorFreqTable.size;
+    unsigned int const sum     = freqTotal(colorFreqTable);
 
     struct BoxVector boxVector;
 
@@ -473,7 +470,7 @@ newBoxVector(tupletable2           const colorfreqtable,
     boxVector.box[0].colorCt = colorCt;
     boxVector.box[0].sum     = sum;
 
-    computeBoxSpread(&boxVector.box[0], colorfreqtable, depth,
+    computeBoxSpread(&boxVector.box[0], colorFreqTable, depth,
                      methodForLargest,
                      &boxVector.box[0].maxdim,
                      &boxVector.box[0].spread);
@@ -497,7 +494,7 @@ destroyBoxVector(struct BoxVector const boxVector) {
 static void
 centerBox(int          const boxStart,
           int          const boxSize,
-          tupletable2  const colorfreqtable,
+          tupletable2  const colorFreqTable,
           unsigned int const depth,
           tuple        const newTuple) {
 
@@ -507,10 +504,10 @@ centerBox(int          const boxStart,
         int minval, maxval;
         unsigned int i;
 
-        minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
+        minval = maxval = colorFreqTable.table[boxStart]->tuple[plane];
 
         for (i = 1; i < boxSize; ++i) {
-            int const v = colorfreqtable.table[boxStart + i]->tuple[plane];
+            int const v = colorFreqTable.table[boxStart + i]->tuple[plane];
             minval = MIN( minval, v);
             maxval = MAX( maxval, v);
         }
@@ -547,7 +544,7 @@ newColorMap(unsigned int const colorCt,
 static void
 averageColors(int          const boxStart,
               int          const boxSize,
-              tupletable2  const colorfreqtable,
+              tupletable2  const colorFreqTable,
               unsigned int const depth,
               tuple        const newTuple) {
 
@@ -560,7 +557,7 @@ averageColors(int          const boxStart,
         sum = 0;
 
         for (i = 0; i < boxSize; ++i)
-            sum += colorfreqtable.table[boxStart+i]->tuple[plane];
+            sum += colorFreqTable.table[boxStart+i]->tuple[plane];
 
         newTuple[plane] = ROUNDDIV(sum, boxSize);
     }
@@ -571,7 +568,7 @@ averageColors(int          const boxStart,
 static void
 averagePixels(int          const boxStart,
               int          const boxSize,
-              tupletable2  const colorfreqtable,
+              tupletable2  const colorFreqTable,
               unsigned int const depth,
               tuple        const newTuple) {
 
@@ -583,7 +580,7 @@ averagePixels(int          const boxStart,
     /* Count the tuples in question */
     n = 0;  /* initial value */
     for (i = 0; i < boxSize; ++i)
-        n += colorfreqtable.table[boxStart + i]->value;
+        n += colorFreqTable.table[boxStart + i]->value;
 
 
     for (plane = 0; plane < depth; ++plane) {
@@ -593,8 +590,8 @@ averagePixels(int          const boxStart,
         sum = 0;
 
         for (i = 0; i < boxSize; ++i)
-            sum += colorfreqtable.table[boxStart+i]->tuple[plane]
-                * colorfreqtable.table[boxStart+i]->value;
+            sum += colorFreqTable.table[boxStart+i]->tuple[plane]
+                * colorFreqTable.table[boxStart+i]->value;
 
         newTuple[plane] = ROUNDDIV(sum, n);
     }
@@ -605,7 +602,7 @@ averagePixels(int          const boxStart,
 static tupletable2
 colormapFromBv(unsigned int      const colorCt,
                struct BoxVector  const boxVector,
-               tupletable2       const colorfreqtable,
+               tupletable2       const colorFreqTable,
                unsigned int      const depth,
                enum MethodForRep const methodForRep) {
     /*
@@ -626,19 +623,19 @@ colormapFromBv(unsigned int      const colorCt,
         case REP_CENTER_BOX:
             centerBox(boxVector.box[boxIdx].index,
                       boxVector.box[boxIdx].colorCt,
-                      colorfreqtable, depth,
+                      colorFreqTable, depth,
                       colormap.table[boxIdx]->tuple);
             break;
         case REP_AVERAGE_COLORS:
             averageColors(boxVector.box[boxIdx].index,
                           boxVector.box[boxIdx].colorCt,
-                          colorfreqtable, depth,
+                          colorFreqTable, depth,
                           colormap.table[boxIdx]->tuple);
             break;
         case REP_AVERAGE_PIXELS:
             averagePixels(boxVector.box[boxIdx].index,
                           boxVector.box[boxIdx].colorCt,
-                          colorfreqtable, depth,
+                          colorFreqTable, depth,
                           colormap.table[boxIdx]->tuple);
             break;
         default:
@@ -654,14 +651,14 @@ colormapFromBv(unsigned int      const colorCt,
 static void
 splitBox(struct BoxVector *    const boxVectorP,
          unsigned int          const boxIdx,
-         tupletable2           const colorfreqtable,
+         tupletable2           const colorFreqTable,
          unsigned int          const depth,
          enum MethodForLargest const methodForLargest,
          enum MethodForSplit   const methodForSplit) {
 /*----------------------------------------------------------------------------
    Split Box 'boxIdx' in the box vector 'boxVector' (so that bv contains one
    more box than it did as input).  Split it so that each new box represents
-   about half of the pixels in the distribution given by 'colorfreqtable' for
+   about half of the pixels in the distribution given by 'colorFreqTable' for
    the colors in the original box, but with distinct colors in each of the two
    new boxes.
 
@@ -672,7 +669,7 @@ splitBox(struct BoxVector *    const boxVectorP,
     unsigned int const sum      = boxVectorP->box[boxIdx].sum;
 
     unsigned int medianIndex;
-    int lowersum;
+    unsigned int lowerSum;
         /* Number of pixels whose value is "less than" the median */
 
 
@@ -685,8 +682,8 @@ splitBox(struct BoxVector *    const boxVectorP,
        parameter to compareplane(), which is called by qsort().
     */
     compareplanePlane = boxVectorP->box[boxIdx].maxdim;
-    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
-          sizeof(colorfreqtable.table[boxStart]),
+    qsort((char*) &colorFreqTable.table[boxStart], boxSize,
+          sizeof(colorFreqTable.table[boxStart]),
           compareplane);
 
     {
@@ -695,9 +692,9 @@ splitBox(struct BoxVector *    const boxVectorP,
         */
         unsigned int i;
 
-        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
-        for (i = 1; i < boxSize - 1 && lowersum < sum/2; ++i) {
-            lowersum += colorfreqtable.table[boxStart + i]->value;
+        lowerSum = colorFreqTable.table[boxStart]->value; /* initial value */
+        for (i = 1; i < boxSize - 1 && lowerSum < sum/2; ++i) {
+            lowerSum += colorFreqTable.table[boxStart + i]->value;
         }
         medianIndex = i;
     }
@@ -706,8 +703,8 @@ splitBox(struct BoxVector *    const boxVectorP,
         struct Box * const oldBoxP = &boxVectorP->box[boxIdx];
 
         oldBoxP->colorCt = medianIndex;
-        oldBoxP->sum     = lowersum;
-        computeBoxSpread(oldBoxP, colorfreqtable, depth, methodForLargest,
+        oldBoxP->sum     = lowerSum;
+        computeBoxSpread(oldBoxP, colorFreqTable, depth, methodForLargest,
                          &oldBoxP->maxdim, &oldBoxP->spread);
     }
     {
@@ -715,8 +712,8 @@ splitBox(struct BoxVector *    const boxVectorP,
 
         newBoxP->index   = boxStart + medianIndex;
         newBoxP->colorCt = boxSize - medianIndex;
-        newBoxP->sum     = sum - lowersum;
-        computeBoxSpread(newBoxP, colorfreqtable, depth, methodForLargest,
+        newBoxP->sum     = sum - lowerSum;
+        computeBoxSpread(newBoxP, colorFreqTable, depth, methodForLargest,
                          &newBoxP->maxdim, &newBoxP->spread);
         ++boxVectorP->boxCt;
     }
@@ -727,21 +724,21 @@ splitBox(struct BoxVector *    const boxVectorP,
 
 
 static void
-mediancut(tupletable2           const colorfreqtable,
+mediancut(tupletable2           const colorFreqTable,
           unsigned int          const depth,
-          int                   const newcolorCt,
+          unsigned int          const newColorCt,
           enum MethodForLargest const methodForLargest,
           enum MethodForRep     const methodForRep,
           enum MethodForSplit   const methodForSplit,
           tupletable2 *         const colormapP) {
 /*----------------------------------------------------------------------------
-   Compute a set of only 'newcolorCt' colors that best represent an
+   Compute a set of only 'newColorCt' colors that best represent an
    image whose pixels are summarized by the histogram
-   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
-   colorfreqtable.table[i] tells the number of pixels in the subject image
+   'colorFreqTable'.  Each tuple in that table has depth 'depth'.
+   colorFreqTable.table[i] tells the number of pixels in the subject image
    have a particular color.
 
-   As a side effect, sort 'colorfreqtable'.
+   As a side effect, sort 'colorFreqTable'.
 -----------------------------------------------------------------------------*/
     struct BoxVector boxVector;
     bool multicolorBoxesExist;
@@ -749,13 +746,13 @@ mediancut(tupletable2           const colorfreqtable,
            there is more splitting we can do.
         */
 
-    boxVector = newBoxVector(colorfreqtable, newcolorCt, depth,
+    boxVector = newBoxVector(colorFreqTable, newColorCt, depth,
                              methodForLargest);
 
-    multicolorBoxesExist = (colorfreqtable.size > 1);
+    multicolorBoxesExist = (colorFreqTable.size > 1);
 
     /* Split boxes until we have enough. */
-    while (boxVector.boxCt < newcolorCt && multicolorBoxesExist) {
+    while (boxVector.boxCt < newColorCt && multicolorBoxesExist) {
         unsigned int boxIdx;
 
         for (boxIdx = 0;
@@ -766,10 +763,10 @@ mediancut(tupletable2           const colorfreqtable,
         if (boxIdx >= boxVector.boxCt)
             multicolorBoxesExist = FALSE;
         else
-            splitBox(&boxVector, boxIdx, colorfreqtable, depth,
+            splitBox(&boxVector, boxIdx, colorFreqTable, depth,
                      methodForLargest, methodForSplit);
     }
-    *colormapP = colormapFromBv(newcolorCt, boxVector, colorfreqtable,
+    *colormapP = colormapFromBv(newColorCt, boxVector, colorFreqTable,
                                 depth, methodForRep);
 
     destroyBoxVector(boxVector);
@@ -834,7 +831,7 @@ static void
 computeHistogram(FILE *         const ifP,
                  int *          const formatP,
                  struct pam *   const freqPamP,
-                 tupletable2 *  const colorfreqtableP) {
+                 tupletable2 *  const colorFreqTableP) {
 /*----------------------------------------------------------------------------
   Make a histogram of the colors in the image stream in the file '*ifP'.
 
@@ -876,13 +873,13 @@ computeHistogram(FILE *         const ifP,
 
         pnm_nextimage(ifP, &eof);
     }
-    colorfreqtableP->table =
+    colorFreqTableP->table =
         pnm_tuplehashtotable(&firstPam, tuplehash, colorCount);
-    colorfreqtableP->size = colorCount;
+    colorFreqTableP->size = colorCount;
 
     pnm_destroytuplehash(tuplehash);
 
-    pm_message("%u colors found", colorfreqtableP->size);
+    pm_message("%u colors found", colorFreqTableP->size);
 
     freqPamP->size   = sizeof(*freqPamP);
     freqPamP->len    = PAM_STRUCT_SIZE(tuple_type);
@@ -899,7 +896,7 @@ computeHistogram(FILE *         const ifP,
 static void
 computeColorMapFromInput(FILE *                const ifP,
                          bool                  const allColors,
-                         int                   const reqColors,
+                         unsigned int          const reqColors,
                          enum MethodForLargest const methodForLargest,
                          enum MethodForRep     const methodForRep,
                          enum MethodForSplit   const methodForSplit,
@@ -925,23 +922,23 @@ computeColorMapFromInput(FILE *                const ifP,
    *formatP and *freqPamP.  (This information is not really
    relevant to our colormap mission; just a fringe benefit).
 -----------------------------------------------------------------------------*/
-    tupletable2 colorfreqtable;
+    tupletable2 colorFreqTable;
 
-    computeHistogram(ifP, formatP, freqPamP, &colorfreqtable);
+    computeHistogram(ifP, formatP, freqPamP, &colorFreqTable);
 
     if (allColors) {
-        *colormapP = colorfreqtable;
+        *colormapP = colorFreqTable;
     } else {
-        if (colorfreqtable.size <= reqColors) {
-            pm_message("Image already has few enough colors (<=%d).  "
+        if (colorFreqTable.size <= reqColors) {
+            pm_message("Image already has few enough colors (<=%u).  "
                        "Keeping same colors.", reqColors);
-            *colormapP = colorfreqtable;
+            *colormapP = colorFreqTable;
         } else {
-            pm_message("choosing %d colors...", reqColors);
-            mediancut(colorfreqtable, freqPamP->depth,
+            pm_message("choosing %u colors...", reqColors);
+            mediancut(colorFreqTable, freqPamP->depth,
                       reqColors, methodForLargest, methodForRep,
                       methodForSplit, colormapP);
-            pnm_freetupletable2(freqPamP, colorfreqtable);
+            pnm_freetupletable2(freqPamP, colorFreqTable);
         }
     }
 }
diff --git a/test/Test-Order b/test/Test-Order
index 214cf4ba..6aab22bd 100644
--- a/test/Test-Order
+++ b/test/Test-Order
@@ -74,6 +74,7 @@ pbmclean.test
 pamcut.test
 pamcat1.test
 pamcat2.test
+pamcat3.test
 pnmcat.test
 pamdice.test
 pamundice.test
diff --git a/test/cut-cat-roundtrip.test b/test/cut-cat-roundtrip.test
index c2799a7b..274cb865 100755
--- a/test/cut-cat-roundtrip.test
+++ b/test/cut-cat-roundtrip.test
@@ -1,6 +1,6 @@
 #! /bin/sh
 # This script tests: pamcut
-# Also requires: pamfile pnmcat pnmpad pnmcrop
+# Also requires: pamfile pamcat pnmpad pnmcrop
 
 tmpdir=${tmpdir:-/tmp}
 
@@ -23,7 +23,7 @@ for i in 0 1 128 224 225
   do
   pamcut -left=$((i+1)) testimg.ppm > ${right_ppm}
   pamcut -right=$i      testimg.ppm > ${left_ppm}
-  pnmcat -lr ${left_ppm} ${right_ppm} | \
+  pamcat -lr ${left_ppm} ${right_ppm} | \
     pamcut -left=0 -width=${width} | cksum
   rm ${left_ppm} ${right_ppm}
   done
@@ -38,7 +38,7 @@ for border in 0 1 128 224 225
   do
   pamcut -left=$((${border}+1)) -width=${width} -pad testimg.ppm > ${right_ppm}
   pamcut -right=${border} -width=${width} -pad testimg.ppm > ${left_ppm}
-  pnmcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \
+  pamcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \
     pamcut -left=$((${width}-${border}-1))  -width=$((${width}*2)) | \
     tee ${padded_ppm} | cksum | tr '\n' ' '
   pnmcrop -black -right ${padded_ppm} | cksum
@@ -53,7 +53,7 @@ for border in 0 1 70 147
   do
   pamcut -top=$((${border}+1)) testimg.ppm > ${bottom_ppm}
   pamcut -bottom=${border}     testimg.ppm > ${top_ppm}
-  pnmcat -tb ${top_ppm} ${bottom_ppm} | \
+  pamcat -tb ${top_ppm} ${bottom_ppm} | \
     pamcut -top=0 -height=${height} | cksum
   rm ${top_ppm} ${bottom_ppm}
   done
@@ -68,7 +68,7 @@ for border in 0 1 70 147
   pamcut -top=$((${border}+1)) -height=${height} -pad testimg.ppm \
     > ${bottom_ppm}
   pamcut -bottom=${border}     -height=${height} -pad testimg.ppm > ${top_ppm}
-  pnmcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \
+  pamcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \
     pamcut -top=$((${height}-${border}-1))  -height=$((${height}*2)) | \
     tee ${padded_ppm} | cksum | tr '\n' ' 'cksum
   pnmcrop -black -bottom ${padded_ppm} | cksum
@@ -94,7 +94,7 @@ for i in 0 1 10 30 50
   do
   pamcut -left=$((i+1)) maze.pbm > ${right_ppm}
   pamcut -right=$i      maze.pbm > ${left_ppm}
-  pnmcat -lr ${left_ppm} ${right_ppm} | \
+  pamcat -lr ${left_ppm} ${right_ppm} | \
     pamcut -left=0 -width=${width} | cksum
   rm ${left_ppm} ${right_ppm}
   done
@@ -110,7 +110,7 @@ for border in 0 1 10 30 50
   do
   pamcut -left=$((${border}+1)) -width=${width} -pad maze.pbm > ${right_ppm}
   pamcut -right=${border} -width=${width} -pad maze.pbm > ${left_ppm}
-  pnmcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \
+  pamcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \
     pamcut -left=$((${width}-${border}-1))  -width=$((${width}*2)) | \
     tee ${padded_ppm} | cksum | tr '\n' ' '
   pnmcrop -black -right ${padded_ppm} | cksum
@@ -125,7 +125,7 @@ for border in 0 1 12 21 31 44
   do
   pamcut -top=$((${border}+1)) maze.pbm > ${bottom_ppm}
   pamcut -bottom=${border}     maze.pbm > ${top_ppm}
-  pnmcat -tb ${top_ppm} ${bottom_ppm} | \
+  pamcat -tb ${top_ppm} ${bottom_ppm} | \
     pamcut -top=0 -height=${height} | cksum
   rm ${top_ppm} ${bottom_ppm}
   done
@@ -139,7 +139,7 @@ for border in 0 1 10 50
   do
   pamcut -top=$((${border}+1)) -height=${height} -pad maze.pbm > ${bottom_ppm}
   pamcut -bottom=${border}     -height=${height} -pad maze.pbm > ${top_ppm}
-  pnmcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \
+  pamcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \
     pamcut -top=$((${height}-${border}-1))  -height=$((${height}*2)) | \
     tee ${padded_ppm} | cksum | tr '\n' ' 'cksum
   pnmcrop -black -bottom ${padded_ppm} | cksum
diff --git a/test/ilbm-roundtrip.ok b/test/ilbm-roundtrip.ok
index 07c44515..2d6fcff0 100644
--- a/test/ilbm-roundtrip.ok
+++ b/test/ilbm-roundtrip.ok
@@ -1,4 +1,4 @@
-Test 1.  Should produce 669206373 10102 five times
+Test 1.  Should produce 669206373 10102 four times
 669206373 10102
 669206373 10102
 669206373 10102
diff --git a/test/ilbm-roundtrip.test b/test/ilbm-roundtrip.test
index c0b684ed..e7a1a4e5 100755
--- a/test/ilbm-roundtrip.test
+++ b/test/ilbm-roundtrip.test
@@ -2,7 +2,7 @@
 # This script tests: ppmtoilbm ilbmtoppm
 # Also requires: pamseq pamdepth pamtopnm pnmremap pnmcolormap
 
-echo "Test 1.  Should produce 669206373 10102 five times"
+echo "Test 1.  Should produce 669206373 10102 four times"
 #Output is PPM raw, 57 by 59  maxval 255
 
 ppmtoilbm maze.pbm | ilbmtoppm | cksum
diff --git a/test/lps-roundtrip.test b/test/lps-roundtrip.test
index 78350d71..24076ef1 100755
--- a/test/lps-roundtrip.test
+++ b/test/lps-roundtrip.test
@@ -30,4 +30,4 @@ pbmtolps -dpi 72 < testgrid.pbm | sed 's/noname/testgrid.pbm/' | \
     cmp -s ${test_ps} -
     echo $?
   
-rm ${test_pgm}
+rm ${test_pgm} ${test_ps}
diff --git a/test/pamarith.ok b/test/pamarith.ok
index 973b8d1b..c2d34dea 100644
--- a/test/pamarith.ok
+++ b/test/pamarith.ok
@@ -193,7 +193,7 @@ Test 3
 3072492814 913
 3072492814 913
 Test 4 (input = output)
-Prints 281226646 481 five times, then 2921940274 59 five times
+Prints 281226646 481 six times, then 2921940274 59 six times
 input image
 281226646 481
 -minimum
diff --git a/test/pamarith.test b/test/pamarith.test
index 34922a90..061f9488 100755
--- a/test/pamarith.test
+++ b/test/pamarith.test
@@ -88,7 +88,7 @@ for fn in "-subtract" "-divide" "-compare" "-shiftleft" "-shiftright"
 rm ${input3_ppm} ${input4_ppm} ${input4_pgm}
 
 echo "Test 4 (input = output)"
-echo "Prints 281226646 481 five times, then 2921940274 59 five times"
+echo "Prints 281226646 481 six times, then 2921940274 59 six times"
 
 for image in maze.pbm ${input1_ppm}
   do
diff --git a/test/pamcat2.test b/test/pamcat2.test
index 6c76568d..ecc7ee5e 100755
--- a/test/pamcat2.test
+++ b/test/pamcat2.test
@@ -28,6 +28,8 @@ pamcat -tb -jc -black ${dotw_pbm} ${check5x5_pbm} ${dotw_pbm} -plain
 pamcat -tb -jr -black ${dotw_pbm} ${check5x5_pbm} ${dotw_pbm} -plain
 pamcat -tb     -black ${dotw_pbm} ${check5x5_pbm} ${dotw_pbm} -plain
 
+rm ${dotw_pbm}
+
 echo "Test 3."
 pbmmake -b 1 1 > ${dotb_pbm}
 
@@ -39,6 +41,10 @@ pamcat -tb -jl -white ${dotb_pbm} ${check5x5_pbm} ${dotb_pbm} |\
 pamcat -tb -jr -white ${dotb_pbm} ${check5x5_pbm} ${dotb_pbm} |\
   pamflip -cw | cksum
 
+rm ${dotb_pbm}
+rm ${check5x5_pbm}
+
+
 echo "Test 4."
 ppmmake rgb:20/40/d0 1 1 | tee ${dot_ppm} | pamcat -lr | cksum
 pamcat -tb  ${dot_ppm} | cksum
@@ -53,4 +59,6 @@ for just in -jleft -jcenter -jright
 do
 pamcat -tb ${just} ${dot_ppm} maze.pbm ${dot_ppm} | cksum
 done
- 
\ No newline at end of file
+
+rm ${dot_ppm}
+
diff --git a/test/pamcat3.ok b/test/pamcat3.ok
new file mode 100644
index 00000000..5dbb2cbc
--- /dev/null
+++ b/test/pamcat3.ok
@@ -0,0 +1,19 @@
+Test 1.   Should print 585134073 133221 four times
+585134073 133221
+585134073 133221
+585134073 133221
+585134073 133221
+Test 2.   Should print 1331083756 152559 three times
+1331083756 152559
+1331083756 152559
+1331083756 152559
+Test Invalid
+Expected failure 1 1
+Expected failure 2 1
+Expected failure 3 1
+Expected failure 4 1
+Expected failure 5 1
+Expected failure 6 1
+Expected failure 7 1
+Expected failure 8 1
+Expected failure 9 1
diff --git a/test/pamcat3.test b/test/pamcat3.test
new file mode 100755
index 00000000..3aed0131
--- /dev/null
+++ b/test/pamcat3.test
@@ -0,0 +1,97 @@
+#! /bin/sh
+# This script tests: pamcat
+# Also requires:
+
+tmpdir=${tmpdir:-/tmp}
+list=${tmpdir}/list
+list2=${tmpdir}/list2
+list3=${tmpdir}/list3
+liste1=${tmpdir}/liste1
+liste2=${tmpdir}/liste2
+liste3=${tmpdir}/liste3
+files="maze.pbm testgrid.pbm testimg.ppm"
+
+ls ${files} | tee ${list} | awk '{print ""; print $0; print ""}' > ${list3}
+sed 's/maze.pbm/-/' ${list} > ${list2}
+
+echo "Test 1.   Should print 585134073 133221 four times"
+pamcat -lr -jt ${files} | cksum
+pamcat -lr -jt -listfile=${list} | cksum
+cat maze.pbm | pamcat -lr -jt -listfile=${list2} | cksum
+pamcat -lr -jt -listfile=${list3} | cksum
+
+echo "Test 2.   Should print 1331083756 152559 three times"
+pamcat -tb -jl ${files} | cksum
+pamcat -tb -jl -listfile=${list} | cksum
+cat ${list} | pamcat -tb -jl -listfile=- | cksum
+
+echo "Test Invalid"
+
+test_out=${tmpdir}/test_out
+
+echo 1>&2
+echo "Invalid command-line argument combinations & listfile content." 1>&2
+echo "Error messages should appear below the line." 1>&2
+echo "-----------------------------------------------------------" 1>&2
+
+# listfile not specified
+pamcat -lr -listfile > ${test_out} || \
+  printf "Expected failure 1"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out}
+
+# listfile does not exist
+pamcat -lr -listfile=`mktemp -u` > ${test_out} || \
+  printf "Expected failure 2"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out}
+
+# listfile empty
+pamcat -lr -listfile=/dev/null > ${test_out} || \
+  printf "Expected failure 3"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out}
+
+# listfile from stdin, empty
+cat /dev/null | pamcat -lr -listfile=- > ${test_out} || \
+  printf "Expected failure 4"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out}
+
+# Files provided from command line in addition to listfile
+pamcat -lr -listfile=${list} testgrid.pbm testgrid.pbm > ${test_out} || \
+  printf "Expected failure 5"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out}
+
+# "-" (stdin) provided from command line in addition to listfile
+pamcat -lr -listfile=${list} - > ${test_out} || \
+  printf "Expected failure 6"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out}
+
+# listfile has nothing but blank lines
+sed 's/^.*$//' ${list3} > ${liste1}  
+pamcat -lr -listfile=${liste1} > ${test_out} || \
+  printf "Expected failure 7"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out} ${liste1}
+
+# Non-existing file in listfile
+( cat ${list} ; mktemp -u ) > ${liste2}
+pamcat -lr -listfile=${liste2} > ${test_out} || \
+  printf "Expected failure 8"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out} ${liste2}
+
+# Multiple instances of "-" in listfile
+( echo "-"; cat ${list}; echo "-"; echo "-" ) > ${liste3}
+pamcat -lr -listfile=${liste3} > ${test_out} || \
+  printf "Expected failure 9"
+  test -s ${test_out}; echo " "$?
+  rm -f ${test_out} ${liste3}
+
+
+rm ${list} ${list2} ${list3}
+
+
diff --git a/test/stdin-pam3.test b/test/stdin-pam3.test
index 02257942..40155671 100755
--- a/test/stdin-pam3.test
+++ b/test/stdin-pam3.test
@@ -72,4 +72,4 @@ for fmt in \
   echo ${testprog2}": "${status4} ${status5} ${status6} $?
   done
 
-rm ${test_pgm}
+rm ${test_pgm} ${out1} ${out2} ${out3} ${out4}
diff --git a/version.mk b/version.mk
index 9b0fe7ec..d8a3f352 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 11
-NETPBM_MINOR_RELEASE = 0
-NETPBM_POINT_RELEASE = 3
+NETPBM_MINOR_RELEASE = 1
+NETPBM_POINT_RELEASE = 0