about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--buildtools/manpage.mk62
-rw-r--r--common.mk2
-rw-r--r--converter/other/fitstopnm.c20
-rw-r--r--converter/other/pamtopng.c47
-rw-r--r--converter/other/pngtxt.c458
-rw-r--r--converter/other/pngtxt.h12
-rw-r--r--converter/other/pngx.c76
-rw-r--r--converter/other/pngx.h15
-rw-r--r--converter/other/pnmtopclxl.c295
-rw-r--r--converter/other/pnmtopng.c2
-rw-r--r--converter/other/pnmtops.c159
-rw-r--r--converter/other/pnmtosgi.c88
-rw-r--r--converter/pbm/escp2topbm.c384
-rw-r--r--converter/pbm/pbmtoescp2.c260
-rw-r--r--converter/pbm/pbmtomacp.c84
-rw-r--r--converter/ppm/ppmtoilbm.c72
-rw-r--r--converter/ppm/ppmtopjxl.c53
-rw-r--r--doc/HISTORY38
-rw-r--r--editor/pnmpad.c262
-rw-r--r--editor/pnmremap.c62
-rw-r--r--lib/Makefile3
-rw-r--r--lib/libpammap.c21
-rw-r--r--lib/util/Makefile1
-rw-r--r--lib/util/runlength.c374
-rw-r--r--lib/util/runlength.h51
-rw-r--r--other/Makefile2
-rw-r--r--other/pamlookup.c182
-rw-r--r--other/pamunlookup.c250
-rw-r--r--test/Test-Order5
-rw-r--r--test/all-in-place.ok3
-rwxr-xr-xtest/all-in-place.test3
-rw-r--r--test/ilbm-roundtrip.ok8
-rwxr-xr-xtest/ilbm-roundtrip.test21
-rw-r--r--test/legacy-names.ok1
-rwxr-xr-xtest/legacy-names.test1
-rw-r--r--test/lookup-roundtrip.ok1
-rwxr-xr-xtest/lookup-roundtrip.test12
-rw-r--r--test/pbm-misc-converters.ok27
-rwxr-xr-xtest/pbm-misc-converters.test40
-rwxr-xr-xtest/png-roundtrip.test3
-rw-r--r--test/png-roundtrip2.ok4
-rwxr-xr-xtest/png-roundtrip2.test22
-rw-r--r--test/sgi-roundtrip.ok2
-rwxr-xr-xtest/sgi-roundtrip.test9
-rw-r--r--test/sunicon-roundtrip.ok1
-rwxr-xr-xtest/sunicon-roundtrip.test8
-rw-r--r--urt/rle_getrow.c686
-rw-r--r--version.mk4
48 files changed, 2694 insertions, 1502 deletions
diff --git a/buildtools/manpage.mk b/buildtools/manpage.mk
index 47d890c3..76116ffe 100644
--- a/buildtools/manpage.mk
+++ b/buildtools/manpage.mk
@@ -48,43 +48,62 @@ MAN1 = \
 	mtvtoppm.1 \
 	neotoppm.1 \
 	palmtopnm.1 \
+	pamaddnoise.1 \
 	pamarith.1 \
+	pambackground.1 \
+	pambayer.1 \
 	pamchannel.1 \
 	pamcomp.1 \
 	pamcut.1 \
 	pamdeinterlace.1 \
+	pamdepth.1 \
 	pamdice.1 \
 	pamditherbw.1 \
 	pamedge.1 \
 	pamendian.1 \
+	pamenlarge.1 \
 	pamfile.1 \
 	pamfixtrunc.1 \
 	pamflip.1 \
 	pamfunc.1 \
 	pamgauss.1 \
+	pamgradient.1 \
 	pamlookup.1 \
+	pammasksharpen.1 \
+	pammixinterlace.1 \
 	pamoil.1 \
 	pamperspective.1 \
+	pampick.1 \
 	pampop9.1 \
+	pamrgbatopng.1 \
 	pamscale.1 \
 	pamseq.1 \
 	pamsharpmap.1 \
 	pamsharpness.1 \
 	pamslice.1 \
+	pamsplit.1 \
 	pamstack.1 \
 	pamstereogram.1 \
 	pamstretch-gen.1 \
 	pamstretch.1 \
 	pamsumm.1 \
 	pamsummcol.1 \
+	pamthreshold.1 \
+	pamtilt.1 \
 	pamtodjvurle.1 \
+	pamtofits.1 \
+	pamtogif.1 \
 	pamtohdiff.1 \
 	pamtohtmltbl.1 \
 	pamtojpeg2k.1 \
 	pamtopfm.1 \
 	pamtopnm.1 \
+	pamtosvg.1 \
 	pamtotga.1 \
+	pamtotiff.1 \
 	pamtouil.1 \
+	pamtoxvmini.1 \
+	pamx.1 \
 	pbmclean.1 \
 	pbmlife.1 \
 	pbmmake.1 \
@@ -107,11 +126,13 @@ MAN1 = \
 	pbmtog3.1 \
 	pbmtogem.1 \
 	pbmtogo.1 \
+	pbmtoibm23xx.1 \
 	pbmtoicon.1 \
 	pbmtolj.1 \
 	pbmtoln03.1 \
 	pbmtolps.1 \
 	pbmtomacp.1 \
+	pbmtomatrixorbital.1 \
 	pbmtomda.1 \
 	pbmtomgr.1 \
 	pbmtomrf.1 \
@@ -136,10 +157,13 @@ MAN1 = \
 	pgmabel.1 \
 	pgmbentley.1 \
 	pgmcrater.1 \
+	pgmdeshadow.1 \
 	pgmedge.1 \
 	pgmenhance.1 \
 	pgmhist.1 \
 	pgmkernel.1 \
+	pgmmake.1 \
+	pgmmedian.1 \
 	pgmminkowski.1 \
 	pgmmorphconv.1 \
 	pgmnoise.1 \
@@ -217,9 +241,13 @@ MAN1 = \
 	ppmchange.1 \
 	ppmcie.1 \
 	ppmcolormask.1 \
+	ppmdcfont.1 \
+	ppmddumpfont.1 \
 	ppmdim.1 \
 	ppmdist.1 \
 	ppmdither.1 \
+	ppmdmkfont.1 \
+	ppmdraw.1 \
 	ppmfade.1 \
 	ppmflash.1 \
 	ppmforge.1 \
@@ -279,6 +307,7 @@ MAN1 = \
 	rawtopgm.1 \
 	rawtoppm.1 \
 	rgb3toppm.1 \
+	rlatopam.1 \
 	rletopnm.1 \
 	sbigtopgm.1 \
 	sgitopnm.1 \
@@ -302,40 +331,11 @@ MAN1 = \
 	yuvsplittoppm.1 \
 	yuvtoppm.1 \
 	zeisstopnm.1 \
-        pamaddnoise.1 \
-        pambackground.1 \
-        pambayer.1 \
-        pamdepth.1 \
-        pamenlarge.1 \
-        pamgradient.1 \
-        pammasksharpen.1 \
-        pammixinterlace.1 \
-        pampick.1 \
-        pamrgbatopng.1 \
-        pamsplit.1 \
-        pamthreshold.1 \
-        pamtilt.1 \
-        pamtofits.1 \
-        pamtogif.1 \
-        pamtosvg.1 \
-        pamtotiff.1 \
-        pamtoxvmini.1 \
-        pamx.1 \
-        pbmtoibm23xx.1 \
-        pbmtomatrixorbital.1 \
-        pgmdeshadow.1 \
-        pgmmake.1 \
-        pgmmedian.1 \
-        ppmdcfont.1 \
-        ppmddumpfont.1 \
-        ppmdmkfont.1 \
-        ppmdraw.1 \
-        rlatopam.1 \
 
 MAN3 = \
-	libnetpbm.3 \
 	libnetpbm_image.3 \
 	libnetpbm_ug.3 \
+	libnetpbm.3 \
 	libpbm.3 \
 	libpgm.3 \
 	libpm.3 \
@@ -348,7 +348,7 @@ MAN5 = \
 	extendedopacity.5 \
 	pam.5 \
 	pbm.5 \
-        pfm.5 \
+	pfm.5 \
 	pgm.5 \
 	pnm.5 \
 	ppm.5 \
diff --git a/common.mk b/common.mk
index a5431727..dd7f4ed3 100644
--- a/common.mk
+++ b/common.mk
@@ -150,7 +150,7 @@ IMPORTINC_LIB_HEADERS := \
 IMPORTINC_LIB_UTIL_HEADERS := \
   bitarith.h bitio.h bitreverse.h filename.h intcode.h floatcode.h io.h \
   matrix.h mallocvar.h \
-  nsleep.h nstring.h pm_c_util.h shhopt.h token.h \
+  nsleep.h nstring.h pm_c_util.h runlength.h shhopt.h token.h \
   wordaccess.h  wordaccess_generic.h wordaccess_64_le.h \
   wordaccess_be_aligned.h wordaccess_be_unaligned.h \
   wordintclz.h
diff --git a/converter/other/fitstopnm.c b/converter/other/fitstopnm.c
index 9a2859bd..bdf5c78a 100644
--- a/converter/other/fitstopnm.c
+++ b/converter/other/fitstopnm.c
@@ -54,7 +54,7 @@
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     const char * inputFileName;
     unsigned int image;  /* zero if unspecified */
     float max;
@@ -75,8 +75,8 @@ struct cmdlineInfo {
 
 
 static void 
-parseCommandLine(int argc, char ** argv, 
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv, 
+                 struct CmdlineInfo * const cmdlineP) {
 /* --------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -120,7 +120,7 @@ parseCommandLine(int argc, char ** argv,
 
     /* Set some defaults the lazy way (using multiple setting of variables) */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (imageSpec) {
@@ -145,6 +145,7 @@ parseCommandLine(int argc, char ** argv,
             pm_error("Too many arguments (%u).  The only non-option argument "
                      "is the input file name.", argc-1);
     }
+    free(option_def);
 }
 
 
@@ -569,7 +570,7 @@ computeMinMax(FILE *             const ifP,
 
 
 static xelval
-determineMaxval(struct cmdlineInfo const cmdline,
+determineMaxval(struct CmdlineInfo const cmdline,
                 valFmt             const valFmt,
                 double             const datamax,
                 double             const datamin) {
@@ -733,9 +734,9 @@ convertRaster(FILE *                const ifP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     unsigned int cols, rows;
     xelval maxval;
@@ -754,7 +755,7 @@ main(int argc, char * argv[]) {
            is undefined
         */
   
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
   
     parseCommandLine(argc, argv, &cmdline);
 
@@ -803,3 +804,6 @@ main(int argc, char * argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/converter/other/pamtopng.c b/converter/other/pamtopng.c
index af284ac0..528184b2 100644
--- a/converter/other/pamtopng.c
+++ b/converter/other/pamtopng.c
@@ -35,7 +35,7 @@
     are filters, palettes, etc. Also, image conversions were removed,
     because those should be done with other NetPBM tools.
 
-  - Add support for iTXt (international language) chunks.
+  - Add ability to create iTXt (international language) chunks.
 */
 
 
@@ -452,57 +452,54 @@ doSrgbChunk(struct pngx *   const pngxP,
 
 
 static void
-doTextChunk(struct pngx * const pngxP,
-            const char *  const textFileName) {
+doTextChunkSet(struct pngx * const pngxP,
+               const char *  const textFileName) {
+
+    bool const ztxt = true;
+    bool const itxt = false;
 
     FILE * tfP;
 
     tfP = pm_openr(textFileName);
     
-    pngtxt_read(pngxP, tfP, false, verbose);
+    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
     
-    if (verbose)
-        pm_message("writing tEXt chunk");
-
     pm_close(tfP);
 }
 
 
 
 static void
-doZtxtChunk(struct pngx * const pngxP,
-            const char *  const textFileName) {
+doZtxtChunkSet(struct pngx * const pngxP,
+               const char *  const textFileName) {
+
+    bool const ztxt = true;
+    bool const itxt = false;
 
     FILE * tfP;
 
     tfP = pm_openr(textFileName);
     
-    pngtxt_read(pngxP, tfP, true, verbose);
+    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
     
-    if (verbose)
-        pm_message("writing zTXt chunk");
-
     pm_close(tfP);
-    
 }
 
 
 
 
 static void
-doItxtChunk (struct pngx * const pngxP,
-             const char *  const textFileName) {
+doItxtChunkSet(struct pngx * const pngxP,
+               const char *  const textFileName) {
+
+    bool const ztxt = true;
+    bool const itxt = true;
 
     FILE * tfP;
 
     tfP = pm_openr(textFileName);
 
-    pm_error("Code to handle an ITXT chunk has not been written yet");
-
-    /* pngtxt_read(pngxP, tfP, true, true, verbose); */
-
-    if (verbose)
-        pm_message("writing iTXt chunk");
+    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
 }
 
 
@@ -669,13 +666,13 @@ writePng(const struct pam * const pamP,
         doSrgbChunk(pngxP, cmdline.srgbintent);
 
     if (cmdline.textSpec)
-        doTextChunk(pngxP, cmdline.text);
+        doTextChunkSet(pngxP, cmdline.text);
 
     if (cmdline.ztxtSpec)
-        doZtxtChunk(pngxP, cmdline.ztxt);
+        doZtxtChunkSet(pngxP, cmdline.ztxt);
 
     if (cmdline.itxtSpec)
-        doItxtChunk(pngxP, cmdline.itxt);
+        doItxtChunkSet(pngxP, cmdline.itxt);
 
     if (cmdline.backgroundSpec)
         doBkgdChunk(pamP, pngxP, cmdline.background);
diff --git a/converter/other/pngtxt.c b/converter/other/pngtxt.c
index bca06d73..e02ee227 100644
--- a/converter/other/pngtxt.c
+++ b/converter/other/pngtxt.c
@@ -1,123 +1,251 @@
+#define HAVE_PNGLIB_WITH_ITXT 0
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+
 #include <png.h>
 
+#include "mallocvar.h"
 #include "nstring.h"
 #include "pngx.h"
 #include "pngtxt.h"
 #include "pm.h"
-#include "mallocvar.h"
-
-#define MAXCOMMENTS 256
 
 
 
 static void
-readOffKey(char           const textline[],
-           unsigned int   const lineLength,
-           unsigned int * const cursorP,
-           char **        const keyP) {
+readToken(char           const textline[],
+          unsigned int   const lineLength,
+          unsigned int * const cursorP,
+          const char **  const tokenP) {
+/*----------------------------------------------------------------------------
+   Read a token from 'textline' (whose length is 'lineLength'), assuming the
+   cursor is positioned to it now, leaving the cursor positioned after it.
 
-    /* Get the comment key */
+   Tokens are delimited by white space.  We don't skip any white space before
+   or after the token.  Ergo, if we are positioned to white space right now,
+   the token we read is a null string.
+-----------------------------------------------------------------------------*/
+    char * tokenBuffer;
     char * cp;
     unsigned int cursor;
 
     cursor = *cursorP;
     
-    MALLOCARRAY(cp, lineLength + 1);  /* leave room for terminating NUL */
-    if (cp == NULL) 
-        pm_error("Unable to allocate memory for text chunks");
-    
-    *keyP = cp;
+    MALLOCARRAY(tokenBuffer, lineLength + 1);
+    /* leave room for terminating NUL */
+    if (tokenBuffer == NULL) 
+        pm_error("Unable to allocate memory for a %u-character "
+                 "text string file line", lineLength);
+
+    cp = &tokenBuffer[0];  /* initial value */
     
     if (textline[0] == '"') {
-        ++cursor;  /* skip past opening " */
+        ++cursor;  /* skip past opening quotation mark */
         while (textline[cursor] != '"') {
-            if (textline[cursor] == '\0') {
-                *cp = '\0';
-                pm_error("Invalid comment file format:  keyword contains "
+            if (cursor >= lineLength)
+                pm_error("Invalid text string file format.  Line ends in "
+                         "the middle of a quoted token.  Text at the end of "
+                         "the line is '%s'", tokenBuffer);
+            if (textline[cursor] == '\0')
+                pm_error("Invalid text string file format:  Token contains "
                          "a NUL character.  Text leading up to the NUL "
-                         "character is '%s'", *keyP);
-            }
+                         "character is '%s'", tokenBuffer);
             *(cp++) = textline[cursor++];
         }
-        ++cursor;  /* skip past closing " */
+        ++cursor;  /* skip past closing quotation mark */
     } else {
-        while (cursor < lineLength && 
-               textline[cursor] != ' '  && textline[cursor] != '\t' &&
-               textline[cursor] != '\0')
+        while ((cursor < lineLength) && 
+               (textline[cursor] != ' ') && (textline[cursor] != '\t')) {
+            
+            if (textline[cursor] == '\0')
+                pm_error("Invalid text string file format:  Token contains "
+                         "a NUL character.  Text leading up to the NUL "
+                         "character is '%s'", tokenBuffer);
             *(cp++) = textline[cursor++];
+        }
     }
     *cp++ = '\0';
 
     *cursorP = cursor;
+
+    *tokenP = tokenBuffer;
 }
 
 
 
 
 static void
-startComment(struct png_text_struct * const commentP, 
-             char                     const textline[],
-             unsigned int             const lineLength,
-             bool                     const compressed) {
+skipWhiteSpace(char           const textline[],
+               unsigned int   const lineLength,
+               unsigned int * const cursorP) {
 /*----------------------------------------------------------------------------
-   Assuming 'textline' is the first line of a comment in a comment file,
-   put the information from it in the comment record *commentP.
-   Use the text on this line as the comment text, even though the true
-   comment text may include text from subsequent continuation lines as
-   well.
+   Move *cursorP past white space (or, for some reason, NUL characters),
+   in 'textline', which is 'lineLength' long.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
 
-   'textline' is not NUL-terminated.  Its length is 'lineLength', and
-   it is at least one character long.  'textline' does not contain a
-   newline character.
+    cursor = *cursorP;  /* initial value */
+
+    while (cursor < lineLength && 
+           (textline[cursor] == ' ' || textline[cursor] == '\t' ||
+            textline[cursor] == '\0'))
+        ++cursor;
 
-   'compressed' means the comment text is compressed.
+    *cursorP = cursor;
+}
+
+
+
+static void
+readTextString(char          const textline[],
+               unsigned int  const lineLength,
+               unsigned int  const startPos,
+               png_charp *   const textStringP,
+               png_size_t *  const textStringLengthP) {
+/*----------------------------------------------------------------------------
+  Extract the text string at 'startPos' in the buffer 'textline', whose
+  length is 'lineLength'.  Return it in newly malloced storage with a
+  pointer to that storage as 'textString' and the size of the text as
+  *textStringLengthP.
 -----------------------------------------------------------------------------*/
-    unsigned int cursor;
+    char * cp;
+
+    MALLOCARRAY(cp, lineLength + 1);  /* incl '\0' */
+    if (!cp) 
+        pm_error("Unable to allocate memory for text chunks");
+
+    memcpy(cp, textline + startPos, lineLength - startPos);
+    cp[lineLength - startPos] = '\0';  /* for safety - not part of text */
+    *textStringP = cp;
+    *textStringLengthP = lineLength - startPos;
+}
+
 
-    /* the following is a not that accurate check on Author or Title */
-    if ((!compressed) || (textline[0] == 'A') || (textline[0] == 'T'))
-        commentP->compression = -1;
-    else
-        commentP->compression = 0;
+
+static void
+startTextChunkEngl(png_text *   const textChunkP, 
+                   char         const textline[],
+                   unsigned int const lineLength,
+                   bool         const isCompressed,
+                   bool         const verbose) {
+/*----------------------------------------------------------------------------
+   Assuming 'textline' is the first line of an entry in an English text
+   string file, put the information from it in the comment record *textChunkP.
+   Use the text on this line as the comment text, even though the true text
+   string may include text from subsequent continuation lines as well.
+
+   'textline' is not NUL-terminated.  Its length is 'lineLength', and it is at
+   least one character long.  'textline' does not contain a newline character.
+
+   'isCompressed' means it is a compressed text chunk.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
 
     cursor = 0;
 
-    readOffKey(textline, lineLength, &cursor, &commentP->key);
+    {
+        const char * key;
+
+        readToken(textline, lineLength, &cursor, &key);
+
+        pngx_setTextKey(textChunkP, key);
+
+        pm_strfree(key);
+    }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    pngx_setTextLang(textChunkP, NULL);
+
+    readTextString(textline, lineLength, cursor, &textChunkP->text,
+                   &textChunkP->text_length);
+
+    textChunkP->compression =
+        isCompressed ? PNG_TEXT_COMPRESSION_zTXt : PNG_TEXT_COMPRESSION_NONE;
+}
+
+
+
+static void
+startTextChunkIntl(png_text *   const textChunkP, 
+                   char         const textline[],
+                   unsigned int const lineLength,
+                   bool         const isCompressed,
+                   bool         const verbose) {
+/*----------------------------------------------------------------------------
+  Assuming 'textline' is the first line of an entry in an international (not
+  English) text string file, put the information from it in the text chunk
+  *textChunkP.  Use the text on this line as the text string, even though the
+  true text string may include text from subsequent continuation lines as
+  well.
+
+  'textline' is not NUL-terminated.  Its length is 'lineLength', and it is at
+  least one character long.  'textline' does not contain a newline character.
+
+  'isInternational' means it is an international (i.e. non-English) text
+  chunk.
+
+  Leave the language attribute (textChunkP->lang) unset.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
+
+    cursor = 0;  /* Initial value */
+
+    {
+        const char * key;
+
+        readToken(textline, lineLength, &cursor, &key);
+
+        pngx_setTextKey(textChunkP, key);
+
+        pm_strfree(key);
+    }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
 
-    /* skip over delimiters between key and comment text */
-    while (cursor < lineLength && 
-           (textline[cursor] == ' ' || textline[cursor] == '\t' ||
-           textline[cursor] == '\0'))
-        ++cursor;
-    
     {
-        /* Get the first line of the comment text */
-        unsigned int const startPos = cursor;
-        char *cp;
-
-        MALLOCARRAY(cp, lineLength+1);  /* leave room for safety NUL */
-        if (!cp) 
-            pm_error("Unable to allocate memory for text chunks");
-
-        memcpy(cp, textline + startPos, lineLength - startPos);
-        cp[lineLength - startPos] = '\0';  /* for safety - not part of text */
-        commentP->text = cp;
-        commentP->text_length = lineLength - startPos;
+        const char * isoLang;
+
+        readToken(textline, lineLength, &cursor, &isoLang);
+
+        pngx_setTextLang(textChunkP, isoLang);
+
+        pm_strfree(isoLang);
     }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    {
+        const char * langKey;
+    
+        readToken(textline, lineLength, &cursor, &langKey);
+
+        pngx_setTextLangKey(textChunkP, langKey);
+
+        pm_strfree(langKey);
+    }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    /* Beginning of text string (continuation lines may follow) */
+    readTextString(textline, lineLength, cursor, &textChunkP->text,
+                   &textChunkP->text_length);
+
+    textChunkP->compression = 
+        isCompressed ? PNG_ITXT_COMPRESSION_zTXt :PNG_ITXT_COMPRESSION_NONE;
 }
 
 
 
 static void
-continueComment(struct png_text_struct * const commentP, 
-                char                     const textline[],
-                unsigned int             const lineLength) {
+continueTextString(png_text *   const textChunkP, 
+                   char         const textline[],
+                   unsigned int const lineLength) {
 /*----------------------------------------------------------------------------
-   Update the comment record *commentP by adding to it the text
-   from textline[], which is a continuation line from a comment file.
+   Update the text chunk *textChunkP by adding to it the text from
+   textline[], which is a continuation line from a text string file.
 
    'textline' is not NUL-terminated.  Its length is 'lineLength', and
    it is at least one character long.  'textline' does not contain a
@@ -126,29 +254,27 @@ continueComment(struct png_text_struct * const commentP,
     unsigned int cursor;  /* cursor into textline[] */
 
     unsigned int const newTextLength =
-        commentP->text_length + lineLength + 1 + 1;
+        textChunkP->text_length + lineLength + 1 + 1;
 
-    REALLOCARRAY(commentP->text, newTextLength);
+    REALLOCARRAY(textChunkP->text, newTextLength);
 
-    if (commentP->text == NULL)
-        pm_error("Unable to allocate %u bytes of memory for comment chunk",
+    if (textChunkP->text == NULL)
+        pm_error("Unable to allocate %u bytes of memory for text string",
                  newTextLength);
 
-    commentP->text[commentP->text_length++] = '\n';
+    textChunkP->text[textChunkP->text_length++] = '\n';
 
-    /* Skip past leading delimiter characters in file line */
-    cursor = 0;
-    while (textline[cursor] == ' ' || textline[cursor] == '\t' ||
-           textline[cursor] == '\0')
-        ++cursor;
+    cursor = 0; 
 
-    memcpy(commentP->text + commentP->text_length,
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    memcpy(textChunkP->text + textChunkP->text_length,
            textline + cursor,
            lineLength - cursor);
 
-    commentP->text_length += lineLength - cursor;
+    textChunkP->text_length += lineLength - cursor;
 
-    commentP->text[commentP->text_length] = '\0';  /* for safety */
+    textChunkP->text[textChunkP->text_length] = '\0';  /* for safety */
 }
 
 
@@ -172,16 +298,16 @@ getFileLine(FILE *         const fileP,
 -----------------------------------------------------------------------------*/
     char * textline;  /* malloc'ed */
     unsigned int cursor;  /* cursor into textline[] */
-    unsigned int allocated;
+    unsigned int allocatedSz;
         /* The number of characters of space that are allocated for
            'textline' 
         */
     bool eol;
     bool gotSomething;
 
-    allocated = 128;  /* initial value */
+    allocatedSz = 128;  /* initial value */
 
-    MALLOCARRAY(textline, allocated);
+    MALLOCARRAY(textline, allocatedSz);
     if (textline == NULL)
         pm_error("Unable to allocate buffer to read a line of a file.");
     
@@ -198,9 +324,10 @@ getFileLine(FILE *         const fileP,
         if (c == '\n' || c == EOF)
             eol = TRUE;
         else {
-            if (cursor > allocated - 1 - 1) { /* leave space for safety NUL */
-                allocated *= 2;
-                REALLOCARRAY(textline, allocated);
+            /* leave space for safety NUL */
+            if (cursor > allocatedSz - 1 - 1) {
+                allocatedSz *= 2;
+                REALLOCARRAY(textline, allocatedSz);
                 if (textline == NULL)
                     pm_error("Unable to allocate buffer to read a line of "
                              "a file.");
@@ -223,90 +350,151 @@ getFileLine(FILE *         const fileP,
 
 static void
 handleArrayAllocation(png_text **    const arrayP,
-                      unsigned int * const allocatedCommentsP,
-                      unsigned int   const commentIdx) {
+                      unsigned int * const allocatedChunkCtP,
+                      unsigned int   const chunkIdx) {
 
-    if (commentIdx >= *allocatedCommentsP) {
-        *allocatedCommentsP *= 2;
-        REALLOCARRAY(*arrayP, *allocatedCommentsP);
+    if (chunkIdx >= *allocatedChunkCtP) {
+        *allocatedChunkCtP *= 2;
+        REALLOCARRAY(*arrayP, *allocatedChunkCtP);
         if (*arrayP == NULL) 
-            pm_error("unable to allocate memory for comment array");
+            pm_error("unable to allocate memory for %u text chunks",
+                *allocatedChunkCtP);
     }
 }
 
 
+
+static bool
+isContinuationLine(const char * const line) {
+/*----------------------------------------------------------------------------
+   Text line 'line', if it is from a text string file, is a continuation of a
+   text string started in an earlier line.
+
+   What identifies a line as a continuation line is that it starts with
+   a space or tab.
+-----------------------------------------------------------------------------*/
+    return line[0] == ' ' || line[0] == '\t';
+}
+
+
+
+static void
+reportChunkCt(bool         const ztxt,
+              bool         const itxt,
+              unsigned int const chunkCt) {
+
+    const char * chunkType;
+
+    if (itxt)
+        chunkType = "iTXt";
+    else {
+        if (ztxt)
+            chunkType = "zTXt";
+        else
+            chunkType = "tEXt";
+    }
+
+    pm_message("Writing %u %s chunks", chunkCt, chunkType);
+}
+
+
+
 /******************************************************************************
                             EXTERNAL SUBROUTINES
 ******************************************************************************/
 
 
 void 
-pngtxt_read(struct pngx * const pngxP,
-            FILE *        const fileP, 
-            bool          const ztxt,
-            bool          const verbose) {
-
-    const char * textline;
-    unsigned int lineLength;
-    unsigned int commentIdx;
-    bool noCommentsYet;
+pngtxt_addChunk(struct pngx * const pngxP,
+                FILE *        const tfP, 
+                bool          const ztxt,
+                bool          const itxt,
+                bool          const verbose) {
+/*----------------------------------------------------------------------------
+   Add text chunks (tEXt, zTXt, or iTXt) to the PNG image represented by
+   *pngxP as directed by file *tfP.
+
+   'itxt' means to make them international language (iTXt) chunks.  Otherwise
+   they are either tEXt or zTXt chunks, depending upon 'ztxt'.
+
+   'ztxt' means to make the text compressed.  If the chunks are not
+   international (i.e. 'itxt' is false), this means the chunks are zTXt chunks
+   instead of 'tEXt' chunks.
+-----------------------------------------------------------------------------*/
+    bool noChunksYet;
     bool eof;
-    png_textp text;  /* An array; one line per element */
-    unsigned int allocatedComments;
+    png_textp text;  /* An array; one chunk per element */
+    unsigned int chunkCt;
+        /* Number of chunks we have completed in the 'text' array */
+    unsigned int allocatedChunkCt;
         /* Number of entries currently allocated for the PNG text array */
 
-    allocatedComments = 256;  /* initial value */
+    /* In an international text string file, the first entry tells the
+       language of all of the chunks, by having key 'Language'.
+    */
+
+    allocatedChunkCt = 256;  /* initial value */
 
-    MALLOCARRAY(text, allocatedComments);
+    MALLOCARRAY(text, allocatedChunkCt);
     if (text == NULL) 
-        pm_error("unable to allocate memory for comment array");
-
-    commentIdx = 0;
-    noCommentsYet = TRUE;
-   
-    eof = FALSE;
-    while (!eof) {
-        getFileLine(fileP, &textline, &lineLength);
+        pm_error("unable to allocate memory for text chunk array");
+
+    for (chunkCt = 0, noChunksYet = true, eof = false; !eof; ) {
+        const char * textline;
+        unsigned int lineLength;
+
+        getFileLine(tfP, &textline, &lineLength);
         if (textline == NULL)
-            eof = TRUE;
+            eof = true;
         else {
             if (lineLength == 0) {
                 /* skip this empty line */
             } else {
-                handleArrayAllocation(&text, &allocatedComments,
-                                      commentIdx);
-                if ((textline[0] != ' ') && (textline[0] != '\t')) {
-                    /* Line doesn't start with white space, which
-                       means it starts a new comment.  
-                    */
-                    if (noCommentsYet) {
-                        /* No previous comment to move past */
-                    } else
-                        ++commentIdx;
-                    noCommentsYet = FALSE;
+                handleArrayAllocation(&text, &allocatedChunkCt, chunkCt);
 
-                    startComment(&text[commentIdx], 
-                                 textline, lineLength, ztxt);
+                if (!isContinuationLine(textline)) {
+                    png_text * textChunkP;
+
+                    if (noChunksYet) {
+                        /* No previous chunk to move past */
+                    } else
+                        ++chunkCt;
+                    noChunksYet = false;
+                    
+                    textChunkP = &text[chunkCt];
+                
+                    if (itxt)
+                        startTextChunkIntl(textChunkP, 
+                                           textline, lineLength, ztxt,
+                                           verbose);
+                    else
+                        startTextChunkEngl(textChunkP, 
+                                           textline, lineLength, ztxt,
+                                           verbose);
                 } else {
+                    png_text * const textChunkP = &text[chunkCt];
+
                     /* Line starts with whitespace, which means it is
-                       a continuation of the current comment.
+                       a continuation of the current text string.
                     */
-                    if (noCommentsYet)
-                        pm_error("Invalid comment file format: "
+                    if (noChunksYet)
+                        pm_error("Invalid text string file format: "
                                  "first line is a continuation line! "
                                  "(It starts with whitespace)");
-                    continueComment(&text[commentIdx], 
-                                    textline, lineLength);
+                    continueTextString(textChunkP, textline, lineLength);
                 }
             }
             pm_strfree(textline);
         }
-    } 
-    if (!noCommentsYet)
-        pngx_setText(pngxP, text, commentIdx + 1);
+    }
+    if (!noChunksYet)
+        ++chunkCt;
 
     if (verbose)
-        pm_message("%d comments placed in text chunk", commentIdx + 1);
+        reportChunkCt(ztxt, itxt, chunkCt);
+
+    if (chunkCt > 0)
+        pngx_setText(pngxP, text, chunkCt);
 
     free(text);
 }
diff --git a/converter/other/pngtxt.h b/converter/other/pngtxt.h
index c83303df..3e6ff2af 100644
--- a/converter/other/pngtxt.h
+++ b/converter/other/pngtxt.h
@@ -1,15 +1,17 @@
 #ifndef PNGTXT_H_INCLUDED
 #define PNGTXT_H_INCLUDED
 
-#include "pm_c_util.h"
+#include <stdbool.h>
+#include <stdio.h>
 #include <png.h>
 
 struct pngx;
 
 void 
-pngtxt_read(struct pngx * const pngxP,
-            FILE *        const tfp, 
-            bool          const ztxt,
-            bool          const verbose);
+pngtxt_addChunk(struct pngx * const pngxP,
+                FILE *        const tfp, 
+                bool          const ztxt,
+                bool          const itxt,
+                bool          const verbose);
 
 #endif
diff --git a/converter/other/pngx.c b/converter/other/pngx.c
index c9f6c7e9..a5171066 100644
--- a/converter/other/pngx.c
+++ b/converter/other/pngx.c
@@ -2,8 +2,18 @@
 #include <png.h>
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "pm.h"
 
+/* <png.h> defines (or doesn't) PNG_iTXt_SUPPORTED to tell us whether libpng
+   has facilities related to PNG iTXt chunks.
+*/
+#ifdef PNG_iTXt_SUPPORTED
+  #define HAVE_PNGLIB_WITH_ITXT 1
+#else
+  #define HAVE_PNGLIB_WITH_ITXT 0
+#endif
+
 #include "pngx.h"
 
 
@@ -504,8 +514,70 @@ pngx_setSigBytes(struct pngx * const pngxP,
 
 
 void
+pngx_setTextKey(png_text *   const textP,
+                const char * const key) {
+
+    /* textP->key is improperly declared in libpng as char *; should
+       be const char *
+    */
+    textP->key = (char *)pm_strdup(key);
+}
+
+
+
+void
+pngx_setTextLang(png_text *   const textP,
+                 const char * const language) {
+
+#if HAVE_PNGLIB_WITH_ITXT
+    if (language)
+        textP->lang = (char *)pm_strdup(language);
+    else
+        textP->lang = NULL;
+#else
+    if (language)
+        pm_error("PNG library does not have ability to create an iTXT "
+                 "chunk (i.e. to create a PNG with text strings in a language "
+                 "other than English)");
+#endif
+}
+
+
+
+void
+pngx_setTextLangKey(png_text *   const textP,
+                    const char * const key) {
+
+#if HAVE_PNGLIB_WITH_ITXT
+    textP->lang_key = (char *)pm_strdup(key);
+#else
+    pm_error("PNG library does not have ability to create an iTXT "
+             "chunk (i.e. to create a PNG with text strings in a language "
+             "other than English)");
+#endif
+}
+
+
+void
+pngx_termText(png_text * const textP) {
+
+    pm_strfree(textP->key);
+
+#if HAVE_PNGLIB_WITH_ITXT
+    if (textP->lang) {
+        pm_strfree(textP->lang);
+        pm_strfree(textP->lang_key);
+    }
+#endif
+
+    free(textP->text);
+}
+
+
+
+void
 pngx_setText(struct pngx * const pngxP,
-             png_textp     const textP,
+             png_text *    const textP,
              unsigned int  const count) {
 
     png_set_text(pngxP->png_ptr, pngxP->info_ptr, textP, count);
@@ -641,7 +713,7 @@ pngx_readEnd(struct pngx * const pngxP) {
 
     /* Note that some of info_ptr is not defined until png_read_end() 
        completes.  That's because it comes from chunks that are at the
-       end of the stream.  In particular, comment and time chunks may
+       end of the stream.  In particular, text and time chunks may
        be at the end.  Furthermore, they may be in both places, in
        which case info_ptr contains different information before and
        after png_read_end().
diff --git a/converter/other/pngx.h b/converter/other/pngx.h
index 1252e32a..cabb0663 100644
--- a/converter/other/pngx.h
+++ b/converter/other/pngx.h
@@ -204,6 +204,21 @@ pngx_setSigBytes(struct pngx * const pngxP,
                  unsigned int  const sigByteCt);
 
 void
+pngx_setTextKey(png_text *   const textP,
+                const char * const key);
+
+void
+pngx_setTextLang(png_text *   const textP,
+                 const char * const language);
+
+void
+pngx_setTextLangKey(png_text *   const textP,
+                    const char * const key);
+
+void
+pngx_termText(png_text * const textP);
+
+void
 pngx_setText(struct pngx * const pngxP,
              png_textp     const textP,
              unsigned int  const count);
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
index ff858575..8cabb614 100644
--- a/converter/other/pnmtopclxl.c
+++ b/converter/other/pnmtopclxl.c
@@ -34,6 +34,7 @@
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "runlength.h"
 
 #include "pclxl.h"
 
@@ -236,162 +237,6 @@ freeCmdline(struct cmdlineInfo const cmdline) {
 
 
 
-#define XY_RLE_FBUFSIZE (1024)
-typedef struct XY_rle {
-    int error;
-    unsigned char buf[128];
-    int bpos;
-    int state;  
-    unsigned char * fbuf;
-    int fbpos;
-    int fbufsize;
-    int fd;
-} XY_rle;
-
-
-
-static void 
-XY_RLEreset(XY_rle * const rleP)  {   
-
-    rleP->state = eSTART;
-    rleP->bpos  = 0;
-    rleP->fbpos = 0;
-    rleP->error = 0;
-}
-
-
-
-static XY_rle * 
-XY_RLEnew(size_t const size) {
-
-    XY_rle * retval;
-    XY_rle * rleP;
-
-    MALLOCVAR(rleP);
-    if (rleP) {
-        rleP->fbufsize = MAX(1024, size);
-        rleP->fbuf = malloc(rleP->fbufsize);
-
-        if (rleP->fbuf) {
-            retval = rleP;
-        } else
-            retval = NULL;
-
-        if (retval == NULL)
-            free(rleP);
-    } else
-        retval = NULL;
-
-    return retval;
-}
-
-
-
-static void
-XY_RLEdelete(XY_rle * const rleP) {
-
-    free(rleP->fbuf);
-    free(rleP);
-}
-
-
-
-static int 
-out(XY_rle * const rleP,
-    int      const count) {
-
-    bool error;
-
-    if (rleP->state == eRLE) {
-        rleP->fbuf[rleP->fbpos++] = -count + 1;
-        rleP->fbuf[rleP->fbpos++] = rleP->buf[0];
-    } else if (rleP->bpos > 0) {
-        rleP->fbuf[rleP->fbpos++] = count - 1;
-        memcpy(rleP->fbuf + rleP->fbpos, rleP->buf, count);
-        rleP->fbpos += count;
-    }
-    if (rleP->fbpos + 129 > rleP->fbufsize) {
-        if (rleP->fbufsize > INT_MAX/1.2)
-            pm_error("Arithmetic overflow during attempt to expand RLE "
-                     "output buffer beyond %u", rleP->fbufsize);
-        rleP->fbufsize *= 1.2; 
-        rleP->fbuf = realloc(rleP->fbuf, rleP->fbufsize);
-        if (rleP->fbuf == NULL) {
-            pm_error("Out of memory while attempting to expand RLE "
-                     "output buffer beyond %u", rleP->fbufsize);
-            rleP->error = -1;
-            rleP->fbpos = 0;
-            error = true;
-        } else
-            error = false;
-    } else
-        error = false;
-
-    rleP->bpos = 0;
-    rleP->state = eSTART;
-
-    return error ? -1 : 0;
-}
-
-
-
-static int
-XY_RLEfinish (XY_rle *rle) {
-    out(rle,rle->bpos);
-    if(rle->error<0) 
-        return rle->error;
-    else
-        return rle->fbpos;
-}
-
-
-
-static  void
-rle_putbyte(XY_rle *      const rleP,
-            unsigned char const u) {
-
-    switch (rleP->state) {
-        case eRLE:
-            if (u != rleP->buf[0])
-                out(rleP, rleP->bpos);
-            break;
-        case eLIT:
-            if (u == rleP->buf[rleP->bpos - 1]
-                && u == rleP->buf[rleP->bpos - 2]) {
-                out(rleP,rleP->bpos - 2);
-                rleP->buf[0] = u;
-                rleP->bpos += 2;
-                rleP->state = eRLE;
-            }   
-            break;
-        case eSTART:
-            if (rleP->bpos == 1) {
-                if (u == rleP->buf[rleP->bpos - 1])
-                    rleP->state = eRLE;
-                else
-                    rleP->state = eLIT;
-            }
-            break;
-    }
-    rleP->buf[rleP->bpos++] = u;
-    if (rleP->bpos == 128) {
-        out(rleP, rleP->bpos);
-    }
-}
-
-
-
-static void
-XY_RLEput(XY_rle *rle,const unsigned char buf[],int count) 
-{
-    int i;
-    for(i=0;i<count;i++) {
-        rle_putbyte(rle,buf[i]);
-    }
-    
-}
-
-
 static int
 XY_Write(int          const fd,
          const void * const buf,
@@ -403,7 +248,7 @@ XY_Write(int          const fd,
     for (len =0, error = false; len < cnt && !error;) {
         ssize_t const rc = write(fd, (char*)buf + len , cnt - len);
         if (rc <= 0)
-            pm_error("Failed to write %u bytes to fd %d", cnt - len, fd);
+            error = true;
         else
             len += rc;
     }
@@ -420,45 +265,73 @@ typedef struct pclGenerator {
     enum ColorDepth colorDepth;
     enum Colorspace colorSpace;
     int width,height;
-    int linelen; /* bytes per line */
-    unsigned char *data;
-    void (*getnextrow)(const struct pclGenerator *, struct pam *);
+    unsigned int linelen;       /* bytes per line */
+    unsigned int paddedLinelen; /* bytes per line + padding */
+    unsigned char * data;
+    unsigned int cursor;
+    void (*getnextrow)(struct pclGenerator *, struct pam *);
 } pclGenerator;
 
 
 
 static void
-pnmToPcllinePackbits(const pclGenerator * const pclGeneratorP,
-                     struct pam *         const pamP) {
+pnmToPcllinePackbits(pclGenerator * const pclGeneratorP,
+                     struct pam *   const pamP) {
 
     tuple * tuplerow;
-    unsigned int pcl_cursor;
     unsigned char accum;
     unsigned char bitmask;
-    unsigned int col;
+    unsigned int col, padCt;
+    unsigned int const padsize =
+        pclGeneratorP->paddedLinelen - pclGeneratorP->linelen;
         
     tuplerow = pnm_allocpamrow(pamP);
 
     pnm_readpamrow(pamP, tuplerow);
 
-    pcl_cursor = 0; bitmask = 0x80; accum = 0x00;
+    bitmask = 0x80; accum = 0x00;
     for (col = 0; col < pamP->width; ++col) {
         if (tuplerow[col][0] == PAM_PBM_WHITE)
             accum |= bitmask;
         bitmask >>= 1;
         if (bitmask == 0) {
-            pclGeneratorP->data[pcl_cursor++] = accum;
+            pclGeneratorP->data[pclGeneratorP->cursor++] = accum;
             bitmask = 0x80; accum = 0x0;
         } 
     }
     if (bitmask != 0x80)
-        pclGeneratorP->data[pcl_cursor++] = accum;
+        pclGeneratorP->data[pclGeneratorP->cursor++] = accum;
+
+    for (padCt = 0; padCt < padsize; ++padCt)
+        pclGeneratorP->data[pclGeneratorP->cursor++] = 0;
 
     pnm_freepamrow(tuplerow);
 }
 
 
 
+static unsigned int
+pclPaddedLinelen(unsigned int const linelen) {
+
+    if (UINT_MAX - 3 < linelen)
+        pm_error("Image too big to process");
+
+    return (((linelen + 3) / 4) * 4);
+}
+
+
+
+static unsigned int
+pclDatabuffSize(unsigned int const paddedLinelen) {
+
+    if (UINT_MAX / 20 < paddedLinelen)
+        pm_error("Image too big to process");
+
+    return (paddedLinelen * 20);
+}
+
+
+
 static void
 createPclGeneratorPackbits(struct pam *    const pamP,
                            pclGenerator ** const pclGeneratorPP) {
@@ -472,10 +345,12 @@ createPclGeneratorPackbits(struct pam *    const pamP,
     pclGeneratorP->colorDepth = e1Bit;
     pclGeneratorP->colorSpace = eGray;
     pclGeneratorP->linelen = (pamP->width+7)/8;
+    pclGeneratorP->paddedLinelen = pclPaddedLinelen(pclGeneratorP->linelen);
     pclGeneratorP->height = pamP->height;
     pclGeneratorP->width = (pamP->width);
 
-    pclGeneratorP->data = malloc(pclGeneratorP->linelen);
+    pclGeneratorP->data =
+        malloc(pclDatabuffSize(pclGeneratorP->paddedLinelen));
     
     if (pclGeneratorP->data == NULL)
         pm_error("Unable to allocate row buffer.");
@@ -488,26 +363,29 @@ createPclGeneratorPackbits(struct pam *    const pamP,
 
 
 static void
-pnmToPcllineWholebytes(const pclGenerator * const pclGeneratorP,
-                       struct pam *         const pamP) {
+pnmToPcllineWholebytes(pclGenerator * const pclGeneratorP,
+                       struct pam *   const pamP) {
 
     tuple * tuplerow;
-    unsigned int pcl_cursor;
-    unsigned int col;
+    unsigned int col, padCt;
+    unsigned int const padsize =
+        pclGeneratorP->paddedLinelen - pclGeneratorP->linelen;
 
     tuplerow = pnm_allocpamrow(pamP);
 
     pnm_readpamrow(pamP, tuplerow);
 
-    pcl_cursor = 0; /* initial value */
-    
     for (col = 0; col < pamP->width; ++col) {
         unsigned int plane;
         for (plane = 0; plane < pamP->depth; ++plane) {
-            pclGeneratorP->data[pcl_cursor++] = 
+            pclGeneratorP->data[pclGeneratorP->cursor++] = 
                 pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
         }
     }
+
+    for(padCt=0; padCt < padsize; ++padCt)
+        pclGeneratorP->data[pclGeneratorP->cursor++] = 0;
+
     pnm_freepamrow(tuplerow);
 }
 
@@ -530,15 +408,16 @@ createPclGeneratorWholebytes(struct pam *    const pamP,
     pclGenP->colorDepth = e8Bit;
     pclGenP->height     = pamP->height;
     pclGenP->width      = pamP->width;
-    pclGenP->linelen    = pamP->width * pamP->depth;
 
     if (UINT_MAX / pamP->width < pamP->depth)
-        pm_error("Image to big to process");
-    else
-        pclGenP->data = malloc(pamP->width * pamP->depth);
-
-    if (pclGenP->data == NULL)
-        pm_error("Unable to allocate row buffer.");
+        pm_error("Image too big to process");
+    else {
+        pclGenP->linelen = pamP->width * pamP->depth;
+        pclGenP->paddedLinelen = pclPaddedLinelen(pclGenP->linelen);
+        pclGenP->data = malloc(pclDatabuffSize(pclGenP->paddedLinelen));
+        if (pclGenP->data == NULL)
+            pm_error("Unable to allocate row buffer.");
+    }
     
     pclGenP->getnextrow = pnmToPcllineWholebytes;
 
@@ -775,30 +654,25 @@ closeDataSource(int const outFd) {
 
 static void
 convertAndWriteRleBlock(int                  const outFd,
-                        const pclGenerator * const pclGeneratorP,
+                        pclGenerator *       const pclGeneratorP,
                         struct pam *         const pamP,
                         int                  const firstLine,
-                        int                  const nlines,
-                        XY_rle *             const rle) {
+                        int                  const lineCt,
+                        unsigned char *      const outbuf) {
 
-    unsigned char const pad[4] = {0,0,0,0};
-    unsigned int const paddedLinelen = ((pclGeneratorP->linelen+3)/4)*4;
-    int rlelen;
+    size_t rlelen;
     unsigned int line;
-    
-    XY_RLEreset(rle);
 
-    for (line = firstLine; line < firstLine + nlines; ++line) {
+    pclGeneratorP->cursor = 0;
+    for (line = firstLine; line < firstLine + lineCt; ++line) {
         pclGeneratorP->getnextrow(pclGeneratorP, pamP);
-        XY_RLEput(rle, pclGeneratorP->data, pclGeneratorP->linelen);
-        XY_RLEput(rle, pad, paddedLinelen - pclGeneratorP->linelen);
     }
-    rlelen = XY_RLEfinish(rle);
-    if (rlelen<0) 
-        pm_error("Error on Making rle");
+
+    pm_rlenc_compressbyte(pclGeneratorP->data, outbuf, PM_RLE_PACKBITS,
+                          pclGeneratorP->paddedLinelen * lineCt, &rlelen);
 
     xl_dataLength(outFd, rlelen); 
-    XY_Write(outFd, rle->fbuf, rlelen);
+    XY_Write(outFd, outbuf, rlelen);
 }
 
 
@@ -810,12 +684,14 @@ convertAndWriteRleBlock(int                  const outFd,
  * ------------------------------------------------------------
  */
 static void 
-convertAndWriteImage(int                  const outFd,
-                     const pclGenerator * const pclGenP,
-                     struct pam *         const pamP) {
+convertAndWriteImage(int            const outFd,
+                     pclGenerator * const pclGenP,
+                     struct pam *   const pamP) {
+
+    size_t const inSize = (pclGenP-> linelen + 3) * 20;
 
     int blockStartLine;
-    XY_rle * rle;
+    unsigned char * outbuf;
 
     xl_ubyte(outFd, eDirectPixel); xl_attr_ubyte(outFd, aColorMapping);
     xl_ubyte(outFd, pclGenP->colorDepth); xl_attr_ubyte(outFd, aColorDepth);
@@ -825,17 +701,12 @@ convertAndWriteImage(int                  const outFd,
     xl_attr_ubyte(outFd, aDestinationSize);   
     XL_Operator(outFd, oBeginImage);
 
-    if (pclGenP->linelen > INT_MAX / 20)
-        pm_error("Image too big");
-    rle = XY_RLEnew(pclGenP->linelen*20);
-    if (!rle) 
-        pm_error("Unable to allocate %d bytes for the RLE buffer",
-                 pclGenP->linelen * 20);
+    pm_rlenc_allocoutbuf(&outbuf, inSize, PM_RLE_PACKBITS);
 
-    blockStartLine = 0;
-    while (blockStartLine < pclGenP->height) {
+    for (blockStartLine = 0; blockStartLine < pclGenP->height; ) {
         unsigned int const blockHeight =
             MIN(20, pclGenP->height-blockStartLine);
+
         xl_uint16(outFd, blockStartLine); xl_attr_ubyte(outFd, aStartLine); 
         xl_uint16(outFd, blockHeight); xl_attr_ubyte(outFd, aBlockHeight);
         xl_ubyte(outFd, eRLECompression); xl_attr_ubyte(outFd, aCompressMode);
@@ -846,10 +717,10 @@ convertAndWriteImage(int                  const outFd,
         */
         XL_Operator(outFd, oReadImage);
         convertAndWriteRleBlock(outFd, pclGenP, pamP,
-                                blockStartLine, blockHeight, rle);
+                                blockStartLine, blockHeight, outbuf);
         blockStartLine += blockHeight;
     }
-    XY_RLEdelete(rle);
+    pm_rlenc_freebuf(outbuf);
     XL_Operator(outFd, oEndImage);
 }
 
@@ -1087,7 +958,7 @@ endPage(int          const outFd,
 
 static void
 convertAndPrintPage(int                  const outFd,
-                    const pclGenerator * const pclGeneratorP,
+                    pclGenerator *       const pclGeneratorP,
                     struct pam *         const pamP,
                     enum MediaSize       const format,
                     unsigned int         const dpi,
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 8cf9f92e..ac171453 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -2858,7 +2858,7 @@ convertpnm(struct cmdlineInfo const cmdline,
 
     /* tEXT and zTXT chunks */
     if (cmdline.text || cmdline.ztxt)
-        pngtxt_read(pngxP, tfP, !!cmdline.ztxt, cmdline.verbose);
+        pngtxt_addChunk(pngxP, tfP, !!cmdline.ztxt, false, cmdline.verbose);
 
     doTimeChunk(cmdline, pngxP);
 
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index 6cd6be95..349c0a66 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -5,16 +5,16 @@
    We produce two main kinds of Postscript program:
 
       1) Use built in Postscript filters /ASCII85Decode, /ASCIIHexDecode,
-	 /RunLengthDecode, and /FlateDecode;
+         /RunLengthDecode, and /FlateDecode;
 
-	 We use methods we learned from Dirk Krause's program Bmeps.
-	 Previous versions used raster encoding code based on Bmeps
-	 code.  This program does not used any code from Bmeps.
+         We use methods we learned from Dirk Krause's program Bmeps.
+         Previous versions used raster encoding code based on Bmeps
+         code.  This program does not used any code from Bmeps.
 
       2) Use our own filters and redefine /readstring .  This is aboriginal
-	 Netpbm code, from when Postscript was young.  The filters are
-	 nearly identical to /ASCIIHexDecode and /RunLengthDecode.  We
-	 use the same raster encoding code with slight modifications.
+         Netpbm code, from when Postscript was young.  The filters are
+         nearly identical to /ASCIIHexDecode and /RunLengthDecode.  We
+         use the same raster encoding code with slight modifications.
 
    (2) is the default.  (1) gives more options, but relies on features
    introduced in Postscript Level 2, which appeared in 1991.  Postcript
@@ -53,6 +53,7 @@
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
+#include "runlength.h"
 
 
 
@@ -398,16 +399,6 @@ writeFileChar(const char * const buffer,
 
 
 
-static void
-writeFileByte(unsigned char const byte,
-              const char *  const name,
-              FILE *        const ofP) {
-
-    writeFile(&byte, 1, name, ofP);
-}
-
-
-
 #define MAX_FILTER_CT 10
     /* The maximum number of filters this code is capable of applying */
 
@@ -502,7 +493,7 @@ initOutputEncoder(OutputEncoder  * const oeP,
     if (rle) {
         oeP->compressRle = true;
         oeP->runlengthRefresh =
-             psFilter ? INT_MAX : bytesPerRow(icols, bitsPerSample);
+             psFilter ? 1024*1024*16 : bytesPerRow(icols, bitsPerSample);
     } else
         oeP->compressRle = false;
 
@@ -629,39 +620,10 @@ flateFilter(FILE *          const ifP,
    In native (non-psfilter) mode, the run length filter must flush at
    the end of every row.  But the entire raster is sent to the run length
    filter as one continuous stream.  The run length filter learns the
-   refresh interval from oeP->runlengthRefresh.
+   refresh interval from oeP->runlengthRefresh.  In ps-filter mode the
+   run length filter ignores row boundaries and flushes every 4096 bytes.
 */
 
-
-static void
-rlePutBuffer (bool            const repeatMode,
-              unsigned int    const count,
-              unsigned char   const repeatitem,
-              unsigned char * const itembuf,
-              FILE *          const fP) {
-/*----------------------------------------------------------------------------
-   Output some RLE data.  There are two forms of output:
-
-     'repeatMode' true: output a repeat sequence, indicating to repeat byte
-     'repeatitem' 'count' times.
-
-     'repeatMode' false: output a non-repeat sequence indicating the
-     'count' characters in itembuf[].
------------------------------------------------------------------------------*/
-    assert(count > 0);
-    assert(count <= 128);
-
-    if (repeatMode) {
-        writeFileByte((257 - count) % 256, "rlePutBuffer", fP);
-        writeFileByte(repeatitem, "rlePutBuffer", fP);
-    } else {
-        writeFileByte(count - 1, "rlePutBuffer", fP);
-        writeFile(itembuf, count, "rlePutBuffer", fP);
-    }
-}
-
-
-
 static FilterFn rleFilter;
 
 static void
@@ -669,97 +631,32 @@ rleFilter (FILE *          const ifP,
            FILE *          const ofP,
            OutputEncoder * const oeP) {
 
-    unsigned int const refresh = oeP->runlengthRefresh;
+    unsigned int const inSize = oeP->runlengthRefresh;
 
     bool eof;
-    bool repeat;
-    unsigned int count;
-    unsigned int incount;
-    unsigned int repeatcount;
-    unsigned char repeatitem;
-    unsigned char itembuf[128];
+    unsigned char * inbuf;
+    unsigned char * outbuf;
+    size_t outSize;
 
-    repeat = true; /* initial value */
-    count = 0; /* initial value */
+    MALLOCARRAY(inbuf, inSize);
+    if (inbuf == NULL)
+        pm_error("Failed to allocate %u bytes of memory for RLE filter",
+                  inSize);
+    pm_rlenc_allocoutbuf(&outbuf, inSize, PM_RLE_PACKBITS);
 
-    for (eof = false, incount = 0; !eof; ) {
-        int rleitem;
+    for (eof = false; !eof; ) {
+        size_t const bytesRead = fread(inbuf, 1, inSize, ifP);
 
-        rleitem = fgetc(ifP);
-        if (rleitem == EOF)
+        if (feof(ifP))
             eof = true;
-        else {
-            ++incount;
+        else if (ferror(ifP) || bytesRead == 0)
+            pm_error("Internal read error: RLE compression");
 
-            if (repeat && count == 0) {
-                /* Still initializing a repeat buf. */
-                itembuf[count++] = repeatitem = rleitem;
-            }
-            else if (repeat) {
-                /* Repeating - watch for end of run. */
-                if (rleitem == repeatitem) {
-                    /* Run continues. */
-                    itembuf[count++] = rleitem;
-                } else {
-                    /* Run ended */
-                    if (count > 2) {
-                        /* Long enough to dump, so dump a repeat-mode buffer
-                           and start a new one.
-                        */
-                        rlePutBuffer(repeat, count, repeatitem, itembuf, ofP);
-                        repeat = true;
-                        count = 0;
-                        itembuf[count++] = repeatitem = rleitem;
-                    } else {
-                        /* Not long enough - convert to non-repeat mode. */
-                        repeat = false;
-                        itembuf[count++] = repeatitem = rleitem;
-                        repeatcount = 1;
-                    }
-                }
-            } else {
-                /* Not repeating - watch for a run worth repeating. */
-                if (rleitem == repeatitem) {
-                    /* Possible run continues. */
-                    ++repeatcount;
-                    if (repeatcount > 3) {
-                        /* Long enough - dump non-repeat part and start
-                           repeat.
-                        */
-                        unsigned int i;
-                        count = count - (repeatcount - 1);
-                        rlePutBuffer(repeat, count, repeatitem, itembuf, ofP);
-                        repeat = true;
-                        count = repeatcount;
-                        for (i = 0; i < count; ++i)
-                            itembuf[i] = rleitem;
-                    } else {
-                        /* Not long enough yet - continue as non-repeat buf. */
-                        itembuf[count++] = rleitem;
-                    }
-                } else {                    /* Broken run. */
-                    itembuf[count++] = repeatitem = rleitem;
-                    repeatcount = 1;
-                }
-            }
-
-            if (incount == refresh) {
-                rlePutBuffer(repeat, count, repeatitem, itembuf, ofP);
-                repeat = true;
-                count = incount = 0;
-            }
-
-            if (count == 128) {
-                rlePutBuffer(repeat, count, repeatitem, itembuf, ofP);
-                repeat = true;
-                count = 0;
-            }
-        }
+        pm_rlenc_compressbyte(inbuf, outbuf, PM_RLE_PACKBITS,
+                              bytesRead, &outSize);
+        writeFile(outbuf, outSize, "rlePutBuffer", ofP);
     }
 
-    if (count > 0)
-        rlePutBuffer(repeat, count, repeatitem, itembuf, ofP);
-
     fclose(ifP);
     fclose(ofP);
 }
diff --git a/converter/other/pnmtosgi.c b/converter/other/pnmtosgi.c
index a8df5328..cc57349f 100644
--- a/converter/other/pnmtosgi.c
+++ b/converter/other/pnmtosgi.c
@@ -17,30 +17,29 @@
 ** Feb 2010 afu
 ** Added dimension check to prevent short int from overflowing
 */
+
+#include <assert.h>
+
 #include "pnm.h"
 #include "sgi.h"
 #include "mallocvar.h"
-
+#include "runlength.h"
 
 
 /*#define DEBUG*/
 
-typedef short       ScanElem;
+typedef uint16_t       ScanElem;
 typedef struct {
     ScanElem *  data;
     long        length;
 } ScanLine;
 
-#define WORSTCOMPR(x)   (2*(x) + 2)
-
-
 #define MAXVAL_BYTE     255
 #define MAXVAL_WORD     65535
 #define INT16MAX        32767
 
 static char storage = STORAGE_RLE;
 static ScanLine * channel[3];
-static ScanElem * rletemp;
 static xel * pnmrow;
 
 
@@ -108,55 +107,13 @@ writeChannels(unsigned int const cols,
             for (col = 0; col < channel[i][row].length; ++col) {
                 (*put)(channel[i][row].data[col]);
             }
+            pm_rlenc_freebuf((unsigned char *) channel[i][row].data);
         }
     }
 }
 
 
 
-static int
-rleCompress(ScanElem *   const inbuf,
-            unsigned int const size) {
-
-    /* slightly modified RLE algorithm from ppmtoilbm.c written by Robert
-       A. Knop (rknop@mop.caltech.edu)
-    */
-
-    int in, out, hold, count;
-    ScanElem *outbuf = rletemp;
-
-    in = out = 0;
-    while (in < size) {
-        if ((in < size-1) && (inbuf[in] == inbuf[in+1])) {
-            /*Begin replicate run*/
-            for (count = 0, hold = in; in < size &&
-                     inbuf[in] == inbuf[hold] && count < 127;
-                 ++in, ++count)
-                ;
-            outbuf[out++] = (ScanElem)(count);
-            outbuf[out++] = inbuf[hold];
-        } else {
-            /*Do a literal run*/
-            hold = out;
-            ++out;
-            count = 0;
-            while (((in >= size-2) && (in < size))
-                   || ((in < size-2) && ((inbuf[in] != inbuf[in+1])
-                                         || (inbuf[in] != inbuf[in+2])))) {
-                outbuf[out++] = inbuf[in++];
-                if (++count >= 127)
-                    break;
-            }
-            outbuf[hold] = (ScanElem)(count | 0x80);
-        }
-    }
-    outbuf[out++] = (ScanElem)0;     /* terminator */
-
-    return out;
-}
-
-
-
 static ScanElem *
 compress(ScanElem *   const tempArg,
          unsigned int const row,
@@ -165,7 +122,13 @@ compress(ScanElem *   const tempArg,
          unsigned int const chanNum,
          long *       const table,
          unsigned int const bpc) {
+/*----------------------------------------------------------------------------
+   Compress a row, putting results in global 'channel' array, in newly
+   allocated storage (which Caller must free).
 
+   Except that if the compression is null compression, we make 'channel'
+   point to existing storage, which Caller must not free.  Yuck.
+-----------------------------------------------------------------------------*/
     ScanElem * retval;
 
     switch (storage) {
@@ -176,16 +139,26 @@ compress(ScanElem *   const tempArg,
         break;
     case STORAGE_RLE: {
         unsigned int const tabrow = chanNum * rows + row;
-        unsigned int const len = rleCompress(tempArg, cols);
-            /* writes result into rletemp */
-        unsigned int i;
+
+        unsigned int len;
+        size_t lenBytes;
         ScanElem * p;
-        
+
+        pm_rlenc_allocoutbuf((unsigned char **) &p, cols, PM_RLE_SGI16);
+
+        pm_rlenc_compressword(tempArg,(unsigned char *) p, PM_RLE_SGI16,
+                              cols, &lenBytes);
+
+        assert((unsigned)lenBytes == lenBytes);
+            /* Guaranteed by pm_rlenc_compressword() */
+
+        len = lenBytes / 2;  /* sizeof(ScanElem) */
         channel[chanNum][row].length = len;
-        MALLOCARRAY(p, len);
+        REALLOCARRAY(p, len);   /* reclaim some space */
+        if (p == NULL)
+            pm_error("realloc failure while reclaiming memory space "
+                     "for output");
         channel[chanNum][row].data = p;
-        for (i = 0; i < len; ++i)
-            p[i] = rletemp[i];
         table[tabrow] = len * bpc;
         retval = tempArg;
     } break;
@@ -213,7 +186,6 @@ buildChannels(FILE *       const ifP,
 
     if (storage != STORAGE_VERBATIM) {
         MALLOCARRAY_NOFAIL(table, channels * rows);
-        MALLOCARRAY_NOFAIL(rletemp, WORSTCOMPR(cols));
     } else
         table = NULL;
 
@@ -247,8 +219,6 @@ buildChannels(FILE *       const ifP,
     }
 
     free(temp);
-    if (table)
-        free(rletemp);
     return table;
 }
 
diff --git a/converter/pbm/escp2topbm.c b/converter/pbm/escp2topbm.c
index 28296da9..632e6345 100644
--- a/converter/pbm/escp2topbm.c
+++ b/converter/pbm/escp2topbm.c
@@ -14,65 +14,266 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+
+** Major changes were made in July 2015 by Akira Urushibata.
+** Most notably the output functions were rewritten.
+** The -plain option is honored as in other programs.
+
+*   Implementation note (July 2015)
+*
+*   The input file does not have a global header.  Image data is divided
+*   into stripes (or data blocks).   Each stripe has a header with
+*   local values for width, height, horizontal/vertical resolution
+*   and compression mode.
+*
+*   We calculate the global height by adding up the local (stripe)
+*   height values, which may vary.
+*
+*   The width value in the first stripe is used throughout; if any
+*   subsequent stripe reports a different value we abort.
+*
+*   We ignore the resolution fields.  Changes in compression mode
+*   are tolerated; they pose no problem.
+*
+*   The official manual says resolution changes within an image are
+*   not allowed.  It does not mention whether changes in stripe height or
+*   width values should be allowed.
+*
+*   A different implementation approach would be to write temporary
+*   PBM files for each stripe and concatenate them at the final stage
+*   with a system call to "pnmcat -tb".  This method has the advantage
+*   of being capable of handling variations in width.
 */
 
-#include <string.h>
 
-#include "pbm.h"
+#include <stdbool.h>
+
 #include "mallocvar.h"
+#include "pbm.h"
+
+#define ESC 033
+
+
+
+static int
+huntEsc(FILE * const ifP) {
+/*-----------------------------------------------------------------------------
+  Hunt for valid start of image stripe in input.
+
+  Return values:
+    ESC: start of image stripe (data block)
+    EOF: end of file
+    0: any other char or sequence
+-----------------------------------------------------------------------------*/
+    int const ch1 = getc(ifP);
+
+    switch (ch1) {
+    case EOF: return EOF;
+    case ESC: {
+        int const ch2 = getc(ifP);
+
+        switch (ch2) {
+        case EOF: return EOF;
+        case '.': return ESC;
+        default:  return 0;
+        }
+    } break;
+    default: return 0;
+    }
+}
+
+
+
+static unsigned char
+readChar(FILE * const ifP) {
+
+    int const ch = getc(ifP);
+
+    if (ch == EOF)
+        pm_error("EOF encountered while reading image data.");
+
+    return (unsigned char) ch;
+}
+
+
+
+static void       
+readStripeHeader(unsigned int * const widthThisStripeP,
+                 unsigned int * const rowsThisStripeP,
+                 unsigned int * const compressionP,
+                 FILE *         const ifP) {
+
+    unsigned char stripeHeader[6];
+    unsigned int widthThisStripe, rowsThisStripe;
+    unsigned int compression;
+
+    if (fread(stripeHeader, sizeof(stripeHeader), 1, ifP) != 1)
+        pm_error("Error reading image data.");
+
+    compression     = stripeHeader[0];
+    /* verticalResolution   = stripeHeader[1]; */
+    /* horizontalResolution = stripeHeader[2]; */
+    rowsThisStripe  = stripeHeader[3];  
+    widthThisStripe = stripeHeader[5] * 256 + stripeHeader[4];
+
+    if (widthThisStripe == 0 || rowsThisStripe == 0)
+        pm_error("Error: Abnormal value in data block header:  "
+                 "Says stripe has zero width or height");
+
+    if (compression != 0 && compression != 1)
+        pm_error("Error: Unknown compression mode %u", compression);
+
+    *widthThisStripeP = widthThisStripe;
+    *rowsThisStripeP  = rowsThisStripe;
+    *compressionP     = compression;
+}
+
+
 
 /* RLE decoder */
-static unsigned int 
-dec_epson_rle(unsigned        const int k, 
-              unsigned        const char * in, 
-              unsigned char * const out) {
+static void
+decEpsonRLE(unsigned int    const blockSize, 
+            unsigned char * const outBuffer,
+            FILE *          const ifP) {
 
-    unsigned int i;
-    unsigned int pos;
     unsigned int dpos;
 
-    pos = 0;  /* initial value */
-    dpos = 0; /* initial value */
+    for (dpos = 0; dpos < blockSize; ) {
+        unsigned char const flag = readChar(ifP);
 
-    while (dpos < k) {
-        if (in[pos] < 128) {
-            for (i = 0; i < in[pos] + 1; ++i)
-                out[dpos+i] = in[pos + i + 1];     
+        if (flag < 128) {
             /* copy through */
-            pos += i + 1;
+
+            unsigned int const nonrunLength = flag + 1;
+
+            unsigned int i;
+
+            for (i = 0; i < nonrunLength; ++i)
+                outBuffer[dpos+i] = readChar(ifP);
+
+            dpos += nonrunLength;
+        } else if (flag == 128) {
+            pm_message("Code 128 detected in compressed input data: ignored");
         } else {
-            for (i = 0; i < 257 - in[pos]; ++i)
-                out[dpos + i] = in[pos + 1];  
             /* inflate this run */
-            pos += 2;
+
+            unsigned int const runLength = 257 - flag;
+            unsigned char const repeatChar = readChar( ifP );
+
+            unsigned int i;
+
+            for (i = 0; i < runLength; ++i)
+                outBuffer[dpos + i] = repeatChar;  
+            dpos += runLength;
         }
-        dpos += i;
     }
-    if(dpos > k)
-      pm_error("Corrupt compressed block"); 
-    return pos;        /* return number of treated input bytes */
+    if (dpos != blockSize)
+      pm_error("Corruption detected in compressed input data");
 }
 
 
 
-int
-main(int    argc,
-     char * argv[]) {
+static void
+processStripeRaster(unsigned char ** const bitarray,
+                    unsigned int     const rowsThisStripe,
+                    unsigned int     const width,
+                    unsigned int     const compression,
+                    FILE *           const ifP,
+                    unsigned int *   const rowIdxP) {
+         
+    unsigned int const initialRowIdx = *rowIdxP;
+    unsigned int const widthInBytes = pbm_packed_bytes(width);
+    unsigned int const blockSize = rowsThisStripe * widthInBytes;
+    unsigned int const margin = compression==1 ? 256 : 0;
+
+    unsigned char * imageBuffer;
+    unsigned int i;
+    unsigned int rowIdx;
 
-    unsigned int const size = 4096; /* arbitrary value */
+    /* We allocate a new buffer each time this function is called.  Add some
+       margin to the buffer for compressed mode to cope with overruns caused
+       by corrupt input data.
+    */
+
+    MALLOCARRAY(imageBuffer, blockSize + margin);
+
+    if (imageBuffer == NULL)
+        pm_error("Failed to allocate buffer for a block of size %u",
+                 blockSize);
+
+    if (compression == 0) {
+        if (fread(imageBuffer, blockSize, 1, ifP) != 1)
+            pm_error("Error reading image data");
+    } else /* compression == 1 */
+        decEpsonRLE(blockSize, imageBuffer, ifP);
+
+    /* Hand over image data to output by pointer assignment */
+    for (i = 0, rowIdx = initialRowIdx; i < rowsThisStripe; ++i)
+        bitarray[rowIdx++] = &imageBuffer[i * widthInBytes];
+
+    *rowIdxP = rowIdx;
+}
 
-    FILE *ifP;
-    unsigned int i, len, pos, opos, width, height;
-    unsigned char *input, *output;
-    const char * fileName;
 
-    pbm_init(&argc, argv);
 
-    MALLOCARRAY(input, size);
-    MALLOCARRAY(output, size);
-    
-    if (input == NULL || output == NULL)
-        pm_error("Cannot allocate memory");
+static void
+expandBitarray(unsigned char *** const bitarrayP,
+               unsigned int   *  const bitarraySizeP) {
+
+    unsigned int const heightIncrement = 5120;
+    unsigned int const heightMax = 5120 * 200;
+        /* 5120 rows is sufficient for US legal at 360 DPI */
+
+    *bitarraySizeP += heightIncrement;
+    if (*bitarraySizeP > heightMax)
+        pm_error("Image too tall");
+    else
+        REALLOCARRAY_NOFAIL(*bitarrayP, *bitarraySizeP); 
+}
+
+
+
+static void
+writePbmImage(unsigned char ** const bitarray,
+              unsigned int     const width,
+              unsigned int     const height) {
+
+    unsigned int row;
+
+    if (height == 0)
+        pm_error("No image");
+
+    pbm_writepbminit(stdout, width, height, 0);
+ 
+    for (row = 0; row < height; ++row) {
+        pbm_cleanrowend_packed(bitarray[row], width);
+        pbm_writepbmrow_packed(stdout, bitarray[row], width, 0);
+    }
+}
+
+
+
+int
+main(int          argc,
+     const char * argv[]) {
+
+    FILE * ifP;
+    unsigned int width;
+        /* Width of the image, or zero to mean width is not yet known.
+           (We get the width from the first stripe in the input; until
+           we've seen that stripe, we don't know the width)
+        */
+    unsigned int height;
+        /* Height of the image as seen so far.  (We process a stripe at a
+           time, increasing this value as we go).
+        */
+    unsigned int rowIdx;
+    unsigned char ** bitarray;
+    unsigned int bitarraySize;
+    const char * fileName;
+    bool eof;
+
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
         pm_error("Too many arguments (%u).  Only argument is filename.",
@@ -85,69 +286,64 @@ main(int    argc,
 
     ifP = pm_openr(fileName);
 
-    /* read the whole file */
-    len = 0;  /* initial value */
-    for (i = 0; !feof(ifP); ++i) {
-        size_t bytesRead;
-        REALLOCARRAY(input, (i+1) * size);
-        if (input == NULL)
-            pm_error("Cannot allocate memory");
-        bytesRead = fread(input + i * size, 1, size, ifP);
-        len += bytesRead;
-    }
+    /* Initialize bitarray */
+    bitarray = NULL;  bitarraySize = 0;
+    expandBitarray(&bitarray, &bitarraySize);
 
-    /* filter out raster data */
-    height = 0;  /* initial value */
-    width  = 0;  /* initial value */
-    pos = 0;     /* initial value */
-    opos = 0;    /* initial value */
-
-    while (pos < len) {
-        /* only ESC sequences are regarded  */
-        if (input[pos] == '\x1b' && input[pos+1] == '.') {
-            unsigned int const k =
-                input[pos+5] * ((input[pos+7] * 256 + input[pos+6] + 7) / 8);
-            unsigned int const margin = 256;
-            if(input[pos+5] == 0)
-                pm_error("Abnormal height value in escape sequence");
-            height += input[pos+5];
-            if(width == 0) /* initialize */
-                width = input[pos+7] * 256 + input[pos+6];
-            else if(width != input[pos+7] * 256 + input[pos+6])
-                pm_error("Abnormal width value in escape sequence");
-
-            REALLOCARRAY(output, opos + k + margin);
-            if (output == NULL)
-                pm_error("Cannot allocate memory");
-
-            switch (input[pos+2]) {
-            case 0:
-                /* copy the data block */
-                memcpy(output + opos, input + pos + 8, k);        
-                pos += k + 8;
-                opos += k;
-                break;
-            case 1: {
-                /* inflate the data block */
-                unsigned int l;
-                l = dec_epson_rle(k,input+pos+8,output+opos);  
-                pos += l + 8;
-                opos += k;
-            }
-                break;
-            default:
-                pm_error("unknown compression mode");
-                break;
+    for (eof = false, width = 0, height = 0, rowIdx = 0; !eof; ) {
+        int const r = huntEsc(ifP);
+
+        if (r == EOF)
+            eof = true;
+        else {
+            if (r == ESC) {
+                unsigned int compression;
+                unsigned int rowsThisStripe;
+                unsigned int widthThisStripe;
+            
+                readStripeHeader(&widthThisStripe, &rowsThisStripe,
+                                 &compression, ifP);
+
+                if (rowsThisStripe == 0)
+                    pm_error("Abnormal data block height value: 0");
+                else if (rowsThisStripe != 24 && rowsThisStripe != 8 &&
+                         rowsThisStripe != 1) {
+                    /* The official Epson manual says valid values are 1, 8,
+                       24 but we just print a warning message and continue if
+                       other values are detected.
+                    */ 
+                    pm_message("Abnormal data block height value: %u "
+                               "(ignoring)",
+                               rowsThisStripe);
+                }
+                if (width == 0) {
+                    /* Get width from 1st stripe header */
+                    width = widthThisStripe;
+                } else if (width != widthThisStripe) {
+                    /* width change not allowed */
+                    pm_error("Error: Width changed in middle of image "
+                             "from %u to %u",
+                             width, widthThisStripe);
+                }
+                height += rowsThisStripe;
+                if (height > bitarraySize)
+                    expandBitarray(&bitarray, &bitarraySize);
+
+                processStripeRaster(bitarray, rowsThisStripe, width,
+                                    compression, ifP, &rowIdx);
+            } else {
+                /* r != ESC; do nothing */
             }
         }
-        else
-            ++pos;      /* skip bytes outside the ESCX sequence */
     }
 
-    pbm_writepbminit(stdout, width, height, 0);
-    fwrite(output, opos, 1, stdout);
-    free(input); free(output);
-    fclose(stdout); fclose(ifP);
+    writePbmImage(bitarray, width, height);
+
+    fclose(stdout);
+    fclose(ifP);
 
     return 0;
 }
+
+
+
diff --git a/converter/pbm/pbmtoescp2.c b/converter/pbm/pbmtoescp2.c
index 7dc6653d..6f284f3c 100644
--- a/converter/pbm/pbmtoescp2.c
+++ b/converter/pbm/pbmtoescp2.c
@@ -1,4 +1,4 @@
-/* pbmtoescp2.c - read a portable bitmap and produce Epson ESC/P2 raster
+ /* pbmtoescp2.c - read a portable bitmap and produce Epson ESC/P2 raster
 **                 graphics output data for Epson Stylus printers
 **
 ** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
@@ -10,45 +10,65 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+**
+** Major changes were made in July 2015 by Akira Urushibata.
+** Added 720 DPI capability.
+** Added -formfeed, -raw and -stripeheight.
+** Replaced Packbits run length encoding function.  (Use library function.)
+*
+*  ESC/P Reference Manual (1997)
+*  ftp://download.epson-europe.com/pub/download/182/epson18162eu.zip
 */
 
-/* I used the Epson ESC/P Reference Manual (1997) in writing this. */
-
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "pbm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "runlength.h"
+#include "pbm.h"
+
+
 
 static char const esc = 033;
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     const char * inputFileName;
     unsigned int resolution;
     unsigned int compress;
+    unsigned int stripeHeight;
+    bool raw;
+    bool formfeed;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 
     optStruct3 opt;
     unsigned int option_def_index = 0;
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def = malloc(100*sizeof(optEntry));
 
-    unsigned int compressSpec, resolutionSpec;
+    unsigned int compressSpec, resolutionSpec, stripeHeightSpec,
+                 rawSpec, formfeedSpec;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;
     opt.allowNegNum = FALSE;
     OPTENT3(0, "compress",     OPT_UINT,    &cmdlineP->compress,    
-            &compressSpec, 0);
+            &compressSpec,    0);
     OPTENT3(0, "resolution",   OPT_UINT,    &cmdlineP->resolution,  
-            &resolutionSpec, 0);
+            &resolutionSpec,  0);
+    OPTENT3(0, "stripeheight", OPT_UINT,    &cmdlineP->stripeHeight,  
+            &stripeHeightSpec, 0);
+    OPTENT3(0, "raw",          OPT_FLAG,    NULL,  
+            &rawSpec,    0);
+    OPTENT3(0, "formfeed",     OPT_FLAG,    NULL,  
+            &formfeedSpec,    0);
     
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
     
     if (argc-1 > 1)
         pm_error("Too many arguments: %d.  "
@@ -62,79 +82,81 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->compress = 1;
 
     if (resolutionSpec) {
-        if (cmdlineP->resolution != 360 && cmdlineP->resolution != 180)
+        if (cmdlineP->resolution != 720 && cmdlineP->resolution != 360 &&
+            cmdlineP->resolution != 180)
             pm_error("Invalid -resolution value: %u.  "
-                     "Only 180 and 360 are valid.", cmdlineP->resolution);
+                     "Only 180, 360 and 720 are valid.", cmdlineP->resolution);
     } else
         cmdlineP->resolution = 360;
 
+    if (stripeHeightSpec) {
+        if (cmdlineP->stripeHeight == 0 ||
+            cmdlineP->stripeHeight > 255)
+            pm_error("Invalid -stripeheight value: %u. "
+                     "Should be 24, 8, or 1, and must be in the range 1-255",
+                     cmdlineP->stripeHeight);
+        else if (cmdlineP->stripeHeight != 24 &&
+                 cmdlineP->stripeHeight != 8  &&
+                 cmdlineP->stripeHeight != 1)
+            pm_message("Proceeding with irregular -stripeheight value: %u. "
+                       "Should be 24, 8, or 1.", cmdlineP->stripeHeight);
+        else if (cmdlineP->resolution == 720 &&
+                 cmdlineP->stripeHeight != 1)
+            /* The official Epson manual mandates single-row stripes for
+               720 dpi high-resolution images.
+            */
+            pm_message("Proceeding with irregular -stripeheight value: %u. "
+                       "Because resolution i 720dpi, should be 1.",
+                        cmdlineP->stripeHeight);
+    } else
+        cmdlineP->stripeHeight = cmdlineP->resolution == 720 ? 1 : 24;
+
+    if (rawSpec && formfeedSpec)
+        pm_error("You cannot specify both -raw and -formfeed");
+    else {
+        cmdlineP->raw = rawSpec ? true : false ;
+        cmdlineP->formfeed = formfeedSpec ? true : false ;
+    }
+
     if (argc-1 == 1)
         cmdlineP->inputFileName = argv[1];
     else
         cmdlineP->inputFileName = "-";
+
+    free(option_def);
 }
 
 
 
-static unsigned int
-enc_epson_rle(unsigned int          const l, 
-              const unsigned char * const src, 
-              unsigned char *       const dest) {
-/*----------------------------------------------------------------------------
-  compress l data bytes from src to dest and return the compressed
-  length
------------------------------------------------------------------------------*/
-    unsigned int i;      /* index */
-    unsigned int state;  /* run state */
-    unsigned int pos;    /* source position */
-    unsigned int dpos;   /* destination position */
-
-    pos = dpos = state  = 0;
-    while ( pos < l )
-    {
-        for (i=0; i<128 && pos+i<l; i++)
-            /* search for begin of a run, smallest useful run is 3
-               equal bytes 
-            */
-            if(src[pos+i]==src[pos+i+1] && src[pos+i]==src[pos+i+2])
-            {
-                state=1;                       /* set run state */
-                break;
-            }
-	if(i)
-	{
-        /* set counter byte for copy through */
-        dest[dpos] = i-1;       
-        /* copy data bytes before run begin or end cond. */
-        memcpy(dest+dpos+1,src+pos,i);    
-        pos+=i; dpos+=i+1;                 /* update positions */
-	}
-    if (state)
-    {
-        for (i=0; src[pos+i]==src[pos+i+1] && i<128 && pos+i<l; i++);
-        /* found the runlength i */
-        dest[dpos]   = 257-i;           /* set counter for byte repetition */
-        dest[dpos+1] = src[pos];        /* set byte to be repeated */
-        pos+=i; dpos+=2; state=0;       /* update positions, reset run state */
-        }
-    }
-    return dpos;
+static void
+writeSetup(unsigned int const hres) {
+
+    /* Set raster graphic mode. */
+    printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1);
+
+    /* Set line spacing in units of 1/360 inches. */
+    printf("%c%c%c", esc, '+', 24 * hres / 10);
 }
 
 
 
 int
-main(int argc, char* argv[]) {
+main(int argc, const char * argv[]) {
 
-    FILE* ifP;
+    FILE * ifP;
     int rows, cols;
     int format;
-    unsigned int row, idx, len;
-    unsigned int h, v;
-    unsigned char *bytes, *cprbytes;
-    struct cmdlineInfo cmdline;
+    unsigned int row;
+    unsigned int idx;
+    unsigned int outColByteCt;
+    unsigned int stripeByteCt;
+    unsigned int hres, vres;
+    unsigned char * inBuff;
+    unsigned char * bitrow[256];
+    unsigned char * compressedData;
+    struct CmdlineInfo cmdline;
     
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -142,51 +164,83 @@ main(int argc, char* argv[]) {
 
     pbm_readpbminit(ifP, &cols, &rows, &format);
 
-    bytes = malloc(24*pbm_packed_bytes(cols)+2);
-    cprbytes = malloc(2*24*pbm_packed_bytes(cols));
-    if (bytes == NULL || cprbytes == NULL)
-        pm_error("Cannot allocate memory");
+    if (cols / 256 > 127)  /* Limit in official Epson manual */
+        pm_error("Image width is too large");
 
-    h = v = 3600/cmdline.resolution;
+    outColByteCt = pbm_packed_bytes(cols);
+    stripeByteCt = cmdline.stripeHeight * outColByteCt;
 
-    /* Set raster graphic mode. */
-    printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1);
+    MALLOCARRAY(inBuff, stripeByteCt);
+    if (inBuff == NULL)
+      pm_error("Out of memory trying to create input buffer of %u bytes",
+               stripeByteCt);
 
-    /* Set line spacing in units of 1/360 inches. */
-    printf("%c%c%c", esc, '+', 24*h/10);
-
-    /* Write out raster stripes 24 rows high. */
-    for (row = 0; row < rows; row += 24) {
-        unsigned int const linesThisStripe = (rows-row<24) ? rows%24 : 24;
-        printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, 
-               v, h, linesThisStripe, 
-               cols%256, cols/256);
-        /* Read pbm rows, each padded to full byte */
-        for (idx = 0; idx < 24 && row+idx < rows; ++idx)
-            pbm_readpbmrow_packed(ifP,bytes+idx*pbm_packed_bytes(cols),
-                                  cols,format);
-        /* Add delimiter to end of rows, using inverse of final
-           data byte to prevent match. */
-        *(bytes+idx*pbm_packed_bytes(cols)) =
-          ~ *(bytes+idx*pbm_packed_bytes(cols)-1);
-
-        /* Write raster data. */
-        if (cmdline.compress != 0) {
-            /* compressed */
-            len = enc_epson_rle(linesThisStripe * pbm_packed_bytes(cols), 
-                                bytes, cprbytes);
-            fwrite(cprbytes,len,1,stdout);
-        } else
-            /* uncompressed */
-            fwrite(bytes, pbm_packed_bytes(cols), linesThisStripe, stdout);    
-
-        if (rows-row >= 24) putchar('\n');
+    if (cmdline.compress != 0)
+        pm_rlenc_allocoutbuf(&compressedData, stripeByteCt, PM_RLE_PACKBITS);
+    else
+        compressedData = NULL;
+
+    for (idx = 0; idx <= cmdline.stripeHeight; ++idx)
+        bitrow[idx]= &inBuff[idx * outColByteCt];
+
+    hres = vres = 3600 / cmdline.resolution;
+        /* Possible values for hres, vres: 20, 10, 5 */
+
+    if (!cmdline.raw)
+        writeSetup(hres);
+
+    /* Write out raster stripes */
+
+    for (row = 0; row < rows; row += cmdline.stripeHeight ) {
+        unsigned int const rowsThisStripe =
+            MIN(rows - row, cmdline.stripeHeight);
+        unsigned int const outCols = outColByteCt * 8;
+
+        if (rowsThisStripe > 0) {
+            unsigned int idx;
+
+            printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, vres, hres,
+                   cmdline.stripeHeight, outCols % 256, outCols / 256);
+
+            /* Read pbm rows, each padded to full byte */
+
+            for (idx = 0; idx < rowsThisStripe; ++idx) {
+                pbm_readpbmrow_packed (ifP, bitrow[idx], cols, format);
+                pbm_cleanrowend_packed(bitrow[idx], cols);
+            }
+
+            /* If at bottom pad with empty rows up to stripe height */
+            if (rowsThisStripe < cmdline.stripeHeight )
+                memset(bitrow[rowsThisStripe], 0,
+                       (cmdline.stripeHeight - rowsThisStripe) * outColByteCt);
+
+            /* Write raster data */
+            if (cmdline.compress != 0) {  /* compressed */
+                size_t compressedDataCt;
+
+                pm_rlenc_compressbyte(inBuff, compressedData, PM_RLE_PACKBITS,
+                                      stripeByteCt, &compressedDataCt);
+                fwrite(compressedData, compressedDataCt, 1, stdout);
+            } else                        /* uncompressed */
+                fwrite(inBuff, stripeByteCt, 1, stdout);
+
+            /* Emit newline to print the stripe */
+            putchar('\n');
+        }
     }
-    free(bytes); free(cprbytes);
+
+    free(inBuff); 
+    free(compressedData);
     pm_close(ifP);
 
-    /* Reset printer. */
-    printf("%c%c", esc, '@');
+    /* Form feed */
+    if (cmdline.formfeed)
+        putchar('\f');
+
+    if (!cmdline.raw) {
+        /* Reset printer. a*/
+        printf("%c%c", esc, '@');
+    }
 
     return 0;
 }
diff --git a/converter/pbm/pbmtomacp.c b/converter/pbm/pbmtomacp.c
index 7ddb1ef5..df5cbb0c 100644
--- a/converter/pbm/pbmtomacp.c
+++ b/converter/pbm/pbmtomacp.c
@@ -41,6 +41,7 @@
 #include "pbm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
+#include "runlength.h"
 #include "macp.h"
 
 #define MIN3(a,b,c)     (MIN((MIN((a),(b))),(c)))
@@ -282,60 +283,6 @@ writeMacpRowPacked(const bit  * const packedBits,
 
 
 static void
-packit(const bit *     const sourceBits,
-       unsigned int    const imageColCharCt,
-       unsigned char * const packedBits,
-       unsigned int  * const packedImageLengthP ) {
-/*--------------------------------------------------------------------------
-  Compress according to packbits algorithm, a byte-level run-length encoding
-  scheme.
-
-  Each row is encoded separately.
-
-  The following code does not produce optimum output when there are 2-byte
-  long sequences between longer ones: the 2-byte run in between does not get
-  packed, using up 3 bytes where 2 would do.
-----------------------------------------------------------------------------*/
-    int charcount, packcount;
-    enum {EQUAL, UNEQUAL} status;
-    bit * count;
-
-    for (packcount = 0, charcount = 0, status = EQUAL;
-         charcount < imageColCharCt;
-        ) {
-        bit const save = sourceBits[charcount++];
-
-        int newcount;
-
-        newcount = 1;
-        while (charcount < imageColCharCt && sourceBits[charcount] == save) {
-            ++charcount;
-            ++newcount;
-        }
-        if (newcount > 2) {
-            count = (unsigned char *) &packedBits[packcount++];
-            *count = 257 - newcount;
-            packedBits[packcount++] = save;
-            status = EQUAL;
-        } else {
-            if (status == EQUAL) {
-                count = (unsigned char *) &packedBits[packcount++];
-                *count = newcount - 1;
-             } else
-                *count += newcount;
-
-            for( ; newcount > 0; --newcount) {
-                packedBits[packcount++] = save;
-            }
-            status = UNEQUAL;
-        }
-    }
-    *packedImageLengthP = packcount;
-}
-
-
-
-static void
 writeMacpRow(bit        * const imageBits,
              unsigned int const leftMarginCharCt,
              unsigned int const imageColCharCt,
@@ -352,31 +299,30 @@ writeMacpRow(bit        * const imageBits,
     else {
         unsigned int const rightMarginCharCt =
             MACP_COLCHARS - leftMarginCharCt - imageColCharCt;
-        unsigned char * const packedBits = malloc(MACP_COLCHARS+1);
-        
-        unsigned int packedImageLength;
+        unsigned char packedBits[MACP_COLCHARS+1];
+        size_t packedImageLength;
 
-        if (packedBits == NULL)
-            pm_error("Failed to get memory for a %u-column row buffer",
-                     MACP_COLCHARS);
+        if (pm_rlenc_maxbytes(MACP_COLCHARS, PM_RLE_PACKBITS)
+            > MACP_COLCHARS + 1)
+            pm_error("INTERNAL ERROR: RLE buffer too small");
 
-        packit(imageBits, imageColCharCt, packedBits, &packedImageLength);
+        pm_rlenc_compressbyte(imageBits, packedBits, PM_RLE_PACKBITS,
+                              imageColCharCt,  &packedImageLength);
 
-        /* Check if we are we better off with compression.  If not, send row
-           unpacked.  See note at top of file.
-        */
-        
         if (packedImageLength +
             (leftMarginCharCt  > 0 ? 1 : 0) * 2 +
             (rightMarginCharCt > 0 ? 1 : 0) * 2
-            < MACP_COLCHARS)
+            < MACP_COLCHARS) {
+            /* It's smaller compressed, so do that */
             writeMacpRowPacked(packedBits, leftMarginCharCt,
                                packedImageLength, rightMarginCharCt, ofP);
-        else /* Extremely rare */
+        } else { /* Extremely rare */
+            /* It's larger compressed, so do it uncompressed.  See note
+               at top of file.
+            */
             writeMacpRowUnpacked(imageBits, leftMarginCharCt, imageColCharCt,
                                  ofP);
-
-        free(packedBits);
+        }
     }
 }
 
diff --git a/converter/ppm/ppmtoilbm.c b/converter/ppm/ppmtoilbm.c
index 9f1aca46..595aa3f4 100644
--- a/converter/ppm/ppmtoilbm.c
+++ b/converter/ppm/ppmtoilbm.c
@@ -38,8 +38,11 @@
 **  - added IFF text chunks
 **
 **  Feb 2010: afu
-**  Added dimension check to prevent short int from overflowing.
-**  
+**  - added dimension check to prevent short int from overflowing.
+**
+**  June 2015: afu
+**  - moved byterun1 (or Packbits) compression to lib/util/runlenth.c
+**  - fixed bug with HAM -nocompress
 **
 **  TODO:
 **  - multipalette capability (PCHG chunk) for std and HAM
@@ -64,6 +67,7 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -71,6 +75,7 @@
 #include "ppm.h"
 #include "ppmfloyd.h"
 #include "pbm.h"
+#include "runlength.h"
 #include "ilbm.h"
 #include "lum.h"
 
@@ -418,46 +423,6 @@ writeBmhd(int const cols,
 
 /************ compression ************/
 
-
-
-static int
-runbyte1(int const size) {
-
-/* runbyte1 algorithm by Robert A. Knop (rknop@mop.caltech.edu) */
-
-    int in,out,count,hold;
-    unsigned char *inbuf = coded_rowbuf;
-    unsigned char *outbuf = compr_rowbuf;
-
-
-    in=out=0;
-    while( in<size ) {
-        if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) {    
-            /*Begin replicate run*/
-            for( count=0, hold=in; 
-                 in < size && inbuf[in] == inbuf[hold] && count < 128; 
-                 in++, count++)
-                ;
-            outbuf[out++]=(unsigned char)(char)(-count+1);
-            outbuf[out++]=inbuf[hold];
-        }
-        else {  /*Do a literal run*/
-            hold=out; out++; count=0;
-            while( ((in>=size-2)&&(in<size)) || 
-                   ((in<size-2) && ((inbuf[in]!=inbuf[in+1])
-                                    ||(inbuf[in]!=inbuf[in+2]))) ) {
-                outbuf[out++]=inbuf[in++];
-                if( ++count>=128 )
-                    break;
-            }
-            outbuf[hold]=count-1;
-        }
-    }
-    return(out);
-}
-
-
-
 static void
 storeBodyrow(unsigned char * const row,
              int             const len) {
@@ -480,22 +445,26 @@ storeBodyrow(unsigned char * const row,
 
 
 
-static int
-compressRow(int const bytes) {
+static unsigned int
+compressRow(unsigned int const bytes) {
 
-    int newbytes;
+    size_t compressedByteCt;
 
-    switch( compmethod ) {
+    switch (compmethod) {
         case cmpByteRun1:
-            newbytes = runbyte1(bytes);
+            pm_rlenc_compressbyte(
+                coded_rowbuf, compr_rowbuf, PM_RLE_PACKBITS, bytes,
+                &compressedByteCt);
             break;
         default:
             pm_error("compressRow(): unknown compression method %d", 
                      compmethod);
     }
-    storeBodyrow(compr_rowbuf, newbytes);
+    storeBodyrow(compr_rowbuf, compressedByteCt);
+
+    assert((unsigned)compressedByteCt == compressedByteCt);
 
-    return newbytes;
+    return (unsigned)compressedByteCt;
 }
 
 
@@ -2020,7 +1989,8 @@ main(int argc, char ** argv) {
             if( ++argn >= argc )
                 pm_error("-camg requires a value");
             value = strtol(argv[argn], &tail, 16);
-            /* TODO: should do some error checking here */
+            if(argv[argn] == tail)
+                pm_error("-camg requires a value");
             viewportmodes |= value;
             gen_camg = 1;
         }
@@ -2329,7 +2299,7 @@ main(int argc, char ** argv) {
         for (i = 0; i < RowBytes(cols); ++i)
             coded_rowbuf[i] = 0;
         if (DO_COMPRESS)
-            MALLOCARRAY_NOFAIL(compr_rowbuf, WORSTCOMPR(RowBytes(cols)));
+            pm_rlenc_allocoutbuf(&compr_rowbuf, RowBytes(cols), PM_RLE_PACKBITS);
     }
     
     switch (mode) {
diff --git a/converter/ppm/ppmtopjxl.c b/converter/ppm/ppmtopjxl.c
index ddf49638..90bcef0f 100644
--- a/converter/ppm/ppmtopjxl.c
+++ b/converter/ppm/ppmtopjxl.c
@@ -20,6 +20,7 @@
 #include "pm_c_util.h"
 #include "nstring.h"
 #include "ppm.h"
+#include "runlength.h"
 
 #define MAXCOLORS 1024
 
@@ -153,52 +154,12 @@ putbits(int const bArg,
             /* remove trailing zeros */
         printf("\033*b"); 
         if (num && !nopack) {            /* TIFF 4.0 packbits encoding */
-            unsigned int i;
-            int start = 0;
-            int next;
-            runcnt[start] = 0;
-            for (i = 1; i < num; ++i) {
-                if (inrow[i] == inrow[i-1]) {
-                    if (runcnt[start] <= 0 && runcnt[start] > -127)
-                        runcnt[start]--;
-                    else
-                        runcnt[start = i] = 0;
-                } else {
-                    if (runcnt[start] >= 0 && runcnt[start] < 127)
-                        runcnt[start]++;
-                    else
-                        runcnt[start = i] = 0;
-                }
-            }
-            for (i = 0, start = 0; i < num; i = next) {
-                int count = runcnt[i];
-                int from = i;
-                if (count >= 0) { /* merge two-byte runs */
-                    for (;;) {
-                        next = i+1+runcnt[i];
-                        if(next >= num || runcnt[next] < 0 ||
-                           count+runcnt[next]+1 > 127)
-                            break;
-                        count += runcnt[next]+1;
-                        i = next;
-                    }
-                }
-                next =  i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]);
-                if (next < num && count > 0 &&
-                    runcnt[next] < 0 && runcnt[next] > -127) {
-                    --count;
-                    --next;
-                    runcnt[next] = runcnt[next+1]-1;
-                }
-                outrow[start++] = count;
-                if (count >= 0) {
-                    while (count-- >= 0)
-                        outrow[start++] = inrow[from++];
-                } else
-                    outrow[start++] = inrow[from];
-            }
-            if (start < num) {
-                num = start;
+            size_t outSize;
+            pm_rlenc_compressbyte(
+                (unsigned char *)inrow, (unsigned char *)outrow,
+                PM_RLE_PACKBITS, num, &outSize); 
+            if (outSize < num) {
+                num = outSize;
                 if (!pack) {
                     printf("2m");
                     pack = true;
diff --git a/doc/HISTORY b/doc/HISTORY
index 38f1cbf3..60edc585 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,32 +4,52 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-15.09.26 BJH  Release 10.71.03
+not yet  BJH  Release 10.72.00
+
+              Add pamunlookup .
+
+              pamtopng: Implement -itxt .
+
+              pamlookup: Add -byplane .
+
+              pbmtoescp2: Add -stripeheight .
+
+              phmtoescp2: Add -resolution .
+
+              pbmtoescp2: Add -formfeed .
+
+              pbmtoescp2: Add -raw .
+
+              pbmtoescp2: Add -resolution=720 .
+
+              pbmtoescp2: Pad output horizontally to a multiple of 8 columns
+              and vertically to a whole stripe to prevent image loss.
 
               fitstopnm: Add message saying you're probably making a mistake
               if you have a 3-D image and don't specify -image.  The third
               dimension is almost always time in practice.
 
-15.08.02 BJH  Release 10.71.02
-
               fitstopnm: Fix -min and -max.  Broken in Netpbm 10.39 (June
               2007).
 
-              Pnmtopclxl: fix buffer overrun causing unpredictable behavior.
+              pnmtopclxl: fix buffer overrun causing unpredictable behavior.
               (Introduced in Netpbm 10.54 (March 2011).
 
-              Pnmtopclxl: fix wild memory access when out of memory.  Always
+              pnmtopclxl: fix wild memory access when out of memory.  Always
               broken (Pnmtopclxl was new in Netpbm 10.6 (July 2002)).
 
-              Pnmtopclxl: fix wild memory access with pathologically large and
+              pnmtopclxl: fix wild memory access with pathologically large and
               uncompressible image.  Always broken (Pnmtopclxl was new in
               Netpbm 10.6 (July 2002)).
 
-              Pnmtopclxl: fix silent output corruption when a file write
+              pnmtopclxl: fix silent output corruption when a file write
               fails.  Always broken (Pnmtopclxl was new in Netpbm 10.6 (July
               2002)).
 
-15.07.02 BJH  Release 10.71.01
+              escp2topbm: Fix -plain.  Always broken (escp2topbm was new in
+              Netpbm 10.18 (September 2003)).
+
+              pnmpad: Add -mheight, -mwidth.
 
               ppmtoilbm: Fix failure with -hamforce and -nocompression.
               Broken in Netpbm 9.12 (March 2001).
@@ -161,7 +181,7 @@ CHANGE HISTORY
               makeman: deal properly with backlash in source.  Thanks Willem
               van Schaik <willem@schaik.com>.  But something was wrong with
               this change and it caused the program always to fail, so
-              we reversed this change in 10.71.01.
+              we reversed this change in 10.72.
 
               Build: don't build and install libjbig and libjasper if we
               are using external versions of them instead.
diff --git a/editor/pnmpad.c b/editor/pnmpad.c
index 1904b687..168b73e0 100644
--- a/editor/pnmpad.c
+++ b/editor/pnmpad.c
@@ -11,6 +11,9 @@
 #include "pnm.h"
 
 #define MAX_WIDTHHEIGHT INT_MAX-10
+    /* The maximum width or height value we can handle without risking
+       arithmetic overflow
+    */
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -31,6 +34,8 @@ struct cmdlineInfo {
     unsigned int bottomSpec;
     float xalign;
     float yalign;
+    unsigned int mwidth;
+    unsigned int mheight;
     unsigned int white;     /* >0: pad white; 0: pad black */
     unsigned int verbose;
 };
@@ -51,7 +56,7 @@ parseCommandLine(int argc, const char ** argv,
 
     unsigned int option_def_index;
     unsigned int blackOpt;
-    unsigned int xalignSpec, yalignSpec;
+    unsigned int xalignSpec, yalignSpec, mwidthSpec, mheightSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -82,6 +87,10 @@ parseCommandLine(int argc, const char ** argv,
             &yalignSpec,           0);
     OPTENT3(0,   "black",     OPT_FLAG,    NULL,
             &blackOpt,           0);
+    OPTENT3(0,   "mwidth",    OPT_UINT,    &cmdlineP->mwidth,
+            &mwidthSpec,         0);
+    OPTENT3(0,   "mheight",   OPT_UINT,    &cmdlineP->mheight,
+            &mheightSpec,        0);
     OPTENT3(0,   "white",     OPT_FLAG,    NULL,
             &cmdlineP->white,    0);
     OPTENT3(0,   "verbose",   OPT_FLAG,    NULL,
@@ -147,6 +156,12 @@ parseCommandLine(int argc, const char ** argv,
     } else
         cmdlineP->yalign = 0.5;
 
+    if (!mwidthSpec)
+        cmdlineP->mwidth = 1;
+
+    if (!mheightSpec)
+        cmdlineP->mheight = 1;
+
     /* get optional input filename */
     if (argc-1 > 1)
         pm_error("This program takes at most 1 parameter.  You specified %d",
@@ -229,13 +244,17 @@ parseCommandLineOld(int argc, const char ** argv,
 
 static void
 validateHorizontalSize(struct cmdlineInfo const cmdline,
-                       unsigned int const cols) {
-
-    unsigned int const xsize = cmdline.xsizeSpec ? cmdline.xsize : 0;
-    unsigned int const lpad  = cmdline.leftSpec  ? cmdline.left  : 0;
-    unsigned int const rpad  = cmdline.rightSpec ? cmdline.right : 0;
+                       unsigned int       const cols) {
+/*----------------------------------------------------------------------------
+   Abort the program if the padding parameters in 'cmdline', applied to
+   an image width 'cols', would result in numbers too large for us to
+   compute with easily.
+-----------------------------------------------------------------------------*/
+    unsigned int const lpad         = cmdline.leftSpec   ? cmdline.left   : 0;
+    unsigned int const rpad         = cmdline.rightSpec  ? cmdline.right  : 0;
+    unsigned int const mwidthMaxPad = cmdline.mwidth - 1;
 
-    if (xsize > MAX_WIDTHHEIGHT)
+    if (cmdline.xsizeSpec && cmdline.xsize > MAX_WIDTHHEIGHT)
         pm_error("The width value you specified is too large.");
 
     if (lpad > MAX_WIDTHHEIGHT)
@@ -244,66 +263,167 @@ validateHorizontalSize(struct cmdlineInfo const cmdline,
     if (rpad > MAX_WIDTHHEIGHT)
         pm_error("The right padding value you specified is too large.");
 
-    if ((double) cols + (double) lpad + (double) rpad > MAX_WIDTHHEIGHT)
-        pm_error("Given padding value(s) makes output width too large.");
+    if ((double) cols +
+        (double) lpad + 
+        (double) rpad +
+        (double) mwidthMaxPad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output width too large "
+                 "for this program to compute");
+
+    if (cmdline.xsizeSpec &&
+        (double) cmdline.xsize + (double) mwidthMaxPad> MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output width too large "
+                 "for this program to compute");
 }
 
 
 
 static void
-computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
-                          unsigned int       const cols,
-                          unsigned int *     const lpadP,
-                          unsigned int *     const rpadP) {
-
-    validateHorizontalSize(cmdline, cols);
-
-    if (cmdline.xsizeSpec) {
-        if (cmdline.leftSpec && cmdline.rightSpec) {
-            if (cmdline.left + cols + cmdline.right < cmdline.xsize) {
-                pm_error("Left padding (%u), and right "
+computePadSizeBeforeMult(unsigned int   const unpaddedSize,
+                         bool           const sizeSpec,
+                         unsigned int   const sizeReq,
+                         bool           const begPadSpec,
+                         unsigned int   const begPadReq,
+                         bool           const endPadSpec,
+                         unsigned int   const endPadReq,
+                         double         const align,
+                         unsigned int * const begPadP,
+                         unsigned int * const endPadP) {
+/*----------------------------------------------------------------------------
+   Compute the padding on each end that would be required if user did not
+   request any "multiple" padding; i.e. he didn't say request e.g. that the
+   output width be a multiple of 10 pixels.
+-----------------------------------------------------------------------------*/
+    if (sizeSpec) {
+        if (begPadSpec && endPadSpec) {
+            if (begPadReq + unpaddedSize + endPadReq < unpaddedSize) {
+                pm_error("Beginning adding (%u), and end "
                          "padding (%u) are insufficient to bring the "
-                         "image width of %d up to %u.",
-                         cmdline.left, cmdline.right, cols, cmdline.xsize);
+                         "image size of %u up to %u.",
+                         begPadReq, endPadReq, unpaddedSize, sizeReq);
             } else {
-                *lpadP = cmdline.left;
-                *rpadP = cmdline.right;
+                *begPadP = begPadReq;
+                *endPadP = endPadReq;
             }
-        } else if (cmdline.leftSpec) {
-            *lpadP = cmdline.left;
-            *rpadP = MAX(cmdline.xsize, cmdline.left + cols) -
-                (cmdline.left + cols);
-        } else if (cmdline.rightSpec) {
-            *rpadP = cmdline.right;
-            *lpadP = MAX(cmdline.xsize, cols + cmdline.right) -
-                (cols + cmdline.right);
+        } else if (begPadSpec) {
+            *begPadP = begPadReq;
+            *endPadP = MAX(sizeReq, unpaddedSize + begPadReq) -
+                (begPadReq + unpaddedSize);
+        } else if (endPadReq) {
+            *endPadP = endPadReq;
+            *begPadP = MAX(sizeReq, unpaddedSize + endPadReq) -
+                (unpaddedSize + endPadReq);
         } else {
-            if (cmdline.xsize > cols) {
-                *lpadP = ROUNDU((cmdline.xsize - cols) * cmdline.xalign);
-                *rpadP = cmdline.xsize - cols - *lpadP;
+            if (sizeReq > unpaddedSize) {
+                *begPadP = ROUNDU((sizeReq - unpaddedSize) * align);
+                *endPadP = sizeReq - unpaddedSize - *begPadP;
             } else {
-                *lpadP = 0;
-                *rpadP = 0;
+                *begPadP = 0;
+                *endPadP = 0;
             }
         }
     } else {
-        *lpadP = cmdline.leftSpec  ? cmdline.left  : 0;
-        *rpadP = cmdline.rightSpec ? cmdline.right : 0;
+        *begPadP = begPadSpec ? begPadReq : 0;
+        *endPadP = endPadSpec ? endPadReq : 0;
+    }
+}
+
+
+
+static void
+computePadSizesOneDim(unsigned int   const unpaddedSize,
+                      bool           const sizeSpec,
+                      unsigned int   const sizeReq,
+                      bool           const begPadSpec,
+                      unsigned int   const begPadReq,
+                      bool           const endPadSpec,
+                      unsigned int   const endPadReq,
+                      double         const align,
+                      unsigned int   const multiple,
+                      unsigned int * const begPadP,
+                      unsigned int * const endPadP) {
+/*----------------------------------------------------------------------------
+   Compute the number of pixels of padding needed before and after a row or
+   column ("before" means on the left side of a row or the top side of a
+   column).  Return them as *padBegP and *padEndP, respectively.
+
+   'unpaddedSize' is the size (width/height) of the row or column before
+   any padding.
+
+   The rest of the inputs are the padding parameters, equivalent to the
+   program's corresponding command line options.
+-----------------------------------------------------------------------------*/
+    unsigned int begPadBeforeMult, endPadBeforeMult;
+        /* The padding we would apply if user did not request multiple
+           padding (such as "make the output a multiple of 10 pixels")
+        */
+
+    computePadSizeBeforeMult(unpaddedSize, sizeSpec, sizeReq,
+                             begPadSpec, begPadReq, endPadSpec, endPadReq,
+                             align,
+                             &begPadBeforeMult, &endPadBeforeMult);
+
+    {
+        unsigned int const sizeBeforeMpad =
+            unpaddedSize + begPadBeforeMult + endPadBeforeMult;
+        unsigned int const paddedSize =
+            ROUNDUP(sizeBeforeMpad, multiple);
+        unsigned int const morePadNeeded = paddedSize - sizeBeforeMpad;
+        unsigned int const totalPadBeforeMult =
+            begPadBeforeMult + endPadBeforeMult;
+        double const begFrac =
+            totalPadBeforeMult > 0 ? 
+            (double)begPadBeforeMult / totalPadBeforeMult :
+            0.0;
+        unsigned int const addlMsizeBegPad = ROUNDU(morePadNeeded * begFrac);
+            /* # of pixels we have to add to the beginning to satisfy
+               user's desire for the final size to be a multiple of something
+            */
+        unsigned int const addlMsizeEndPad = morePadNeeded - addlMsizeBegPad;
+            /* Analogous to 'addlMsizeBegPad' */
+
+        *begPadP = begPadBeforeMult + addlMsizeBegPad;
+        *endPadP = endPadBeforeMult + addlMsizeEndPad;
     }
 }
 
 
 
 static void
+computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
+                          unsigned int       const cols,
+                          unsigned int *     const lpadP,
+                          unsigned int *     const rpadP) {
+
+    validateHorizontalSize(cmdline, cols);
+
+    computePadSizesOneDim(cols,
+                          cmdline.xsizeSpec > 0, cmdline.xsize,
+                          cmdline.leftSpec > 0, cmdline.left,
+                          cmdline.rightSpec > 0, cmdline.right,
+                          cmdline.xalign,
+                          cmdline.mwidth,
+                          lpadP, rpadP);
+}
+
+
+
+static void
 validateVerticalSize(struct cmdlineInfo const cmdline,
                      unsigned int       const rows) {
+/*----------------------------------------------------------------------------
+   Abort the program if the padding parameters in 'cmdline', applied to
+   an image width 'cols', would result in numbers too large for us to
+   compute with easily.
+-----------------------------------------------------------------------------*/
+    unsigned int const tpad          =
+        cmdline.topSpec    ?  cmdline.top     : 0;
+    unsigned int const bpad          =
+        cmdline.bottomSpec ?  cmdline.bottom  : 0;
+    unsigned int const mheightMaxPad = cmdline.mheight - 1;
 
-    unsigned int const ysize = cmdline.ysizeSpec  ? cmdline.ysize  : 0;
-    unsigned int const tpad  = cmdline.topSpec    ? cmdline.top    : 0;
-    unsigned int const bpad  = cmdline.bottomSpec ? cmdline.bottom : 0;
-
-    if (ysize > MAX_WIDTHHEIGHT)
-        pm_error("The height value you specified is too large.");
+    if (cmdline.ysizeSpec && cmdline.ysize > MAX_WIDTHHEIGHT)
+        pm_error("The width value you specified is too large.");
 
     if (tpad > MAX_WIDTHHEIGHT)
         pm_error("The top padding value you specified is too large.");
@@ -311,8 +431,17 @@ validateVerticalSize(struct cmdlineInfo const cmdline,
     if (bpad > MAX_WIDTHHEIGHT)
         pm_error("The bottom padding value you specified is too large.");
 
-    if ((double) rows + (double) tpad + (double) bpad > MAX_WIDTHHEIGHT)
-        pm_error("Given padding value(s) makes output height too large.");
+    if ((double) rows +
+        (double) tpad +
+        (double) bpad +
+        (double) mheightMaxPad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output height too large "
+            "for this program to compute");
+
+    if (cmdline.ysizeSpec &&
+        (double) cmdline.ysize && (double) mheightMaxPad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output height too large "
+            "for this program to compute");
 }
 
 
@@ -325,38 +454,13 @@ computeVerticalPadSizes(struct cmdlineInfo const cmdline,
 
     validateVerticalSize(cmdline, rows);
 
-    if (cmdline.ysizeSpec) {
-        if (cmdline.topSpec && cmdline.bottomSpec) {
-            if (cmdline.bottom + rows + cmdline.top < cmdline.ysize) {
-                pm_error("Top padding (%u), and bottom "
-                         "padding (%u) are insufficient to bring the "
-                         "image height of %d up to %u.",
-                         cmdline.top, cmdline.bottom, rows, cmdline.ysize);
-            } else {
-                *tpadP = cmdline.top;
-                *bpadP = cmdline.bottom;
-            }
-        } else if (cmdline.topSpec) {
-            *tpadP = cmdline.top;
-            *bpadP = MAX(cmdline.ysize, cmdline.top + rows) -
-                (cmdline.top + rows);
-        } else if (cmdline.bottomSpec) {
-            *bpadP = cmdline.bottom;
-            *tpadP = MAX(cmdline.ysize, rows + cmdline.bottom) -
-                (rows + cmdline.bottom);
-        } else {
-            if (cmdline.ysize > rows) {
-                *bpadP = ROUNDU((cmdline.ysize - rows) * cmdline.yalign);
-                *tpadP = cmdline.ysize - rows - *bpadP;
-            } else {
-                *bpadP = 0;
-                *tpadP = 0;
-            }
-        }
-    } else {
-        *bpadP = cmdline.bottomSpec ? cmdline.bottom : 0;
-        *tpadP = cmdline.topSpec    ? cmdline.top    : 0;
-    }
+    computePadSizesOneDim(rows,
+                          cmdline.ysizeSpec > 0, cmdline.ysize,
+                          cmdline.topSpec > 0, cmdline.top,
+                          cmdline.bottomSpec > 0, cmdline.bottom,
+                          cmdline.yalign,
+                          cmdline.mheight,
+                          tpadP, bpadP);
 }
 
 
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index b2448cbb..ed758aa3 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -35,13 +35,13 @@
 
 #define MAXCOLORS 32767u
 
-enum missingMethod {
+enum MissingMethod {
     MISSING_FIRST,
     MISSING_SPECIFIED,
     MISSING_CLOSE
 };
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -49,7 +49,7 @@ struct cmdlineInfo {
     const char * mapFilespec;    /* Filespec of colormap file */
     unsigned int floyd;   /* Boolean: -floyd/-fs option */
     unsigned int norandom;
-    enum missingMethod missingMethod;
+    enum MissingMethod missingMethod;
     char * missingcolor;      
         /* -missingcolor value.  Null if not specified */
     unsigned int verbose;
@@ -58,8 +58,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+parseCommandLine (int argc, const char ** argv,
+                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -83,24 +83,24 @@ parseCommandLine (int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
     
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "floyd",        OPT_FLAG,   
-            NULL,                       &cmdlineP->floyd, 0);
-    OPTENT3(0,   "fs",           OPT_FLAG,   
-            NULL,                       &cmdlineP->floyd, 0);
-    OPTENT3(0,   "nofloyd",      OPT_FLAG,   
-            NULL,                       &nofloyd, 0);
-    OPTENT3(0,   "nofs",         OPT_FLAG,   
-            NULL,                       &nofloyd, 0);
-    OPTENT3(0,   "norandom",     OPT_FLAG,   
+    OPTENT3(0,   "floyd",          OPT_FLAG,   
+            NULL,                       &cmdlineP->floyd,    0);
+    OPTENT3(0,   "fs",             OPT_FLAG,   
+            NULL,                       &cmdlineP->floyd,    0);
+    OPTENT3(0,   "nofloyd",        OPT_FLAG,   
+            NULL,                       &nofloyd,            0);
+    OPTENT3(0,   "nofs",           OPT_FLAG,   
+            NULL,                       &nofloyd,            0);
+    OPTENT3(0,   "norandom",       OPT_FLAG,   
             NULL,                       &cmdlineP->norandom, 0);
     OPTENT3(0,   "firstisdefault", OPT_FLAG,   
-            NULL,                       &firstisdefault, 0);
-    OPTENT3(0,   "mapfile",      OPT_STRING, 
-            &cmdlineP->mapFilespec,    &mapfileSpec, 0);
-    OPTENT3(0,   "missingcolor", OPT_STRING, 
-            &cmdlineP->missingcolor,   &missingSpec, 0);
-    OPTENT3(0, "verbose",        OPT_FLAG,   NULL,                  
-            &cmdlineP->verbose,        0 );
+            NULL,                       &firstisdefault,     0);
+    OPTENT3(0,   "mapfile",        OPT_STRING, 
+            &cmdlineP->mapFilespec,    &mapfileSpec,         0);
+    OPTENT3(0,   "missingcolor",   OPT_STRING, 
+            &cmdlineP->missingcolor,   &missingSpec,         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 */
@@ -108,7 +108,7 @@ parseCommandLine (int argc, char ** argv,
 
     cmdlineP->missingcolor = NULL;  /* default value */
     
-    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (cmdlineP->floyd && nofloyd)
@@ -135,6 +135,8 @@ parseCommandLine (int argc, char ** argv,
         cmdlineP->inputFilespec = "-";
     else
         cmdlineP->inputFilespec = argv[1];
+
+    free(option_def);
 }
 
 
@@ -1103,7 +1105,14 @@ processMapFile(const char *   const mapFileName,
                tupletable *   const colormapP,
                unsigned int * const colormapSizeP,
                tuple *        const firstColorP) {
+/*----------------------------------------------------------------------------
+   Read a color map from the file named 'mapFileName'.  It's a map that
+   associates each color in that file with a unique whole number.  Return the
+   map as *colormapP, with the number of entries in it as *colormapSizeP.
 
+   Also determine the first color (top left) in the map file and return that
+   as *firstColorP.
+-----------------------------------------------------------------------------*/
     FILE * mapfile;
     struct pam mappam;
     tuple ** maptuples;
@@ -1156,9 +1165,9 @@ getSpecifiedMissingColor(struct pam * const pamP,
 
 
 int
-main(int argc, char * argv[] ) {
+main(int argc, const char * argv[] ) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     struct pam outpamCommon;
         /* Describes the output images.  Width and height fields are
@@ -1181,7 +1190,7 @@ main(int argc, char * argv[] ) {
            color (i.e. we'll choose an approximate match from the map).
         */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -1217,3 +1226,6 @@ main(int argc, char * argv[] ) {
 
     return 0;
 }
+
+
+
diff --git a/lib/Makefile b/lib/Makefile
index fa73d194..0097a04e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -46,6 +46,7 @@ LIBOBJECTS_X = \
   util/matrix.o \
   util/nsleep.o \
   util/nstring.o \
+  util/runlength.o \
   util/shhopt.o \
   util/token.o \
   util/vasprintf.o \
@@ -57,7 +58,7 @@ INTERFACE_HEADERS = colorname.h \
 	pam.h pamdraw.h pammap.h pbm.h pbmfont.h \
 	pgm.h pm.h pm_gamma.h pm_system.h pnm.h \
 	ppm.h ppmcmap.h ppmdfont.h ppmdraw.h ppmfloyd.h \
-	util/mallocvar.h util/shhopt.h \
+	util/mallocvar.h util/runlength.h util/shhopt.h \
 
 DATAFILES = rgb.txt
 
diff --git a/lib/libpammap.c b/lib/libpammap.c
index 55e1d3ff..22224913 100644
--- a/lib/libpammap.c
+++ b/lib/libpammap.c
@@ -75,7 +75,7 @@ pnm_createtuplehash(void) {
 void
 pnm_destroytuplehash(tuplehash const tuplehash) {
 
-    int i;
+    unsigned int i;
 
     /* Free the chains */
 
@@ -156,7 +156,13 @@ pnm_lookuptuple(struct pam *    const pamP,
                 const tuple           searchval, 
                 int *           const foundP, 
                 int *           const retvalP) {
-    
+/*----------------------------------------------------------------------------
+   Return as *revtvalP the index of the tuple value 'searchval' in the
+   tuple hash 'tuplehash'.
+
+   But iff the tuple value isn't in the hash, return *foundP == false
+   and nothing as *retvalP.
+-----------------------------------------------------------------------------*/
     unsigned int const hashvalue = pnm_hashtuple(pamP, searchval);
     struct tupleint_list_item * p;
     struct tupleint_list_item * found;
@@ -221,7 +227,16 @@ pnm_addtuplefreqoccurrence(struct pam *   const pamP,
                            tuple          const value,
                            tuplehash      const tuplefreqhash,
                            int *          const firstOccurrenceP) {
+/*----------------------------------------------------------------------------
+  Tally one more occurence of the tuple value 'value' to the tuple frequencey
+  hash 'tuplefreqhash', adding the tuple to the hash if it isn't there
+  already.
+
+  Allocate new space for the tuple value and the hash chain element.
 
+  If we can't allocate space for the new hash chain element, abort the
+  program.
+-----------------------------------------------------------------------------*/
     unsigned int const hashvalue = pnm_hashtuple(pamP, value);
             
     struct tupleint_list_item * p;
@@ -735,3 +750,5 @@ pam_colorname(struct pam *         const pamP,
     sprintf(colorname, "#%02x%02x%02x", r, g, b);
     return colorname;
 }
+
+
diff --git a/lib/util/Makefile b/lib/util/Makefile
index c8522a04..02119edf 100644
--- a/lib/util/Makefile
+++ b/lib/util/Makefile
@@ -19,6 +19,7 @@ UTILOBJECTS = \
   matrix.o \
   nsleep.o \
   nstring.o \
+  runlength.o \
   shhopt.o \
   token.o \
   vasprintf.o \
diff --git a/lib/util/runlength.c b/lib/util/runlength.c
new file mode 100644
index 00000000..e5c60db0
--- /dev/null
+++ b/lib/util/runlength.c
@@ -0,0 +1,374 @@
+/*=============================================================================
+                                  runlength.c
+===============================================================================
+  "Packbits" run-length encoding and variants.
+
+  Copyright (C) 2015 by Akira Urushibata (afu@wta.att.ne.jp).
+
+  Permission to use, copy, modify, and distribute this software and its
+  documentation for any purpose and without fee is hereby granted, provided
+  that the above copyright notice appear in all copies and that both that
+  copyright notice and this permission notice appear in supporting
+  documentation.  This software is provided "as is" without express or implied
+  warranty.
+
+  Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm
+  originally by Robert A. Knop (rknop@mop.caltech.edu).
+
+  Those who wish to incorporate the code herein into their own programs are
+  strongly discouraged from removing the comments within borders consisting
+  of "+" marks.  This is a practical consideration based on code inspection
+  and bug fixes of various programs which use run-length compression.
+
+===============================================================================
+
+  Packbits is a simple yet efficient simple run-length compression method.  It
+  has a provision for uncompressed sequences which allows efficient encoding
+  of noisy input.
+
+  A survey of netpbm source code in 2015 found Packbits encoding in the
+  following Netpbm programs: pbmtoescp2, pbmtomacp, pnmtopalm, pnmtopclxl,
+  pnmtops, ppmtoilbm and ppmtopjxl.
+ 
+  Packbits is an option in the TIFF standard; pamtotiff can generate TIFF
+  images that use Packbits compression, but does so via code in the TIFF
+  library (not part of Netpbm).
+
+  Variants of Packbits are employed in pnmtosgi,  pamtopdbimg,  pbmtoppa
+  and  pbmtogo.
+
+  All the above programs formerly did the same compression through different
+  code.  This redundancy bloated the Netpbm package and made maintenance
+  difficult.  This maintenance difficulty surfaced as an issue when tests with
+  valgrind revealed multiple memory-related problems in the above programs.
+  Indeed, just determining which programs do this compression by this method
+  was not simple because of differences in terminology.  "Packbits" is often
+  called "run length encoding."  In ppmtoilbm the name is "byterun1."
+
+  Today, all Netpbm programs that do Packbits compression use the facilities
+  in this file for it.
+=============================================================================*/
+
+#include <string.h>
+
+#include "pm.h"
+#include "pm_c_util.h"
+#include "runlength.h"
+#include "mallocvar.h"
+
+
+
+static const char * const errorUndefinedMode =
+    "Internal error: compression mode %u not supported";
+
+
+
+/*-----------------------------------------------------------------------------
+   Run length encoding
+
+   In this simple run-length encoding scheme, compressed and uncompressed
+   strings follow a single index or "flag" byte N.  There are several
+   variations, differing in the format of the flag byte, maximum string length
+   and element size (byte or word).
+   
+   In the most widely used version, Packbits, the meaning of the flag byte N
+   is defined as follows:
+
+    0-127 means the next N+1 bytes are uncompressed.
+    129-255 means the next byte is to be repeated 257-N times.
+    128 is not used.
+
+   The name "Packbits" is misleading: it packs bytes, not bits.
+-----------------------------------------------------------------------------*/
+
+
+
+void
+pm_rlenc_compressbyte(const unsigned char * const inbuf,
+                      unsigned char       * const outbuf,
+                      enum pm_RleMode       const mode,
+                      size_t                const inSize,
+                      size_t              * const outputSizeP) {
+/*-----------------------------------------------------------------------------
+  Compress the contents of input buffer 'inbuf' with Packbits encoding into
+  output buffer 'outbuf'.  'inSize' is the number of bytes of data in 'inbuf'.
+  Return as *outputSizeP the number of bytes we put in 'outbuf'.
+
+  'outbuf' should be allocated with pm_rlenc_allocoutbuf().
+
+  Always encode 3-byte repeat sequences.
+
+  Encode 2-byte repeat sequences only when they are at the start of the block.
+  This ensures that the output is never unnecessary bloated.
+
+  Original algorithm by Robert A. Knop (rknop@mop.caltech.edu)
+-----------------------------------------------------------------------------*/
+    unsigned int const maxRun = 128;
+
+    size_t inCurs, outCurs;
+
+    if (mode != PM_RLE_PACKBITS)
+        pm_error(errorUndefinedMode, mode);
+
+    for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
+        if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
+            /*Begin replicate run*/
+            size_t const hold = inCurs;
+            size_t count;
+            for (count = 0;
+                 inCurs < inSize &&
+                     inbuf[inCurs] == inbuf[hold] &&
+                     count < maxRun; 
+                 ++inCurs, ++count)
+                ;
+
+            outbuf[outCurs++] = (unsigned char)(257 - count);
+            outbuf[outCurs++] = inbuf[hold];
+        } else {
+            /*Do a non-run*/
+            size_t const hold = outCurs;
+            size_t count;
+            ++outCurs;
+            count = 0;
+            while(((inCurs + 2 >= inSize) && (inCurs < inSize) ) || 
+                  ((inCurs + 2 <  inSize) &&
+                   ((inbuf[inCurs] != inbuf[inCurs+1]  )
+                    || (inbuf[inCurs] != inbuf[inCurs+2]  ) ) )    ) {
+                outbuf[outCurs++] = inbuf[inCurs++];
+                if (++count >= 128)
+                    break;
+            }
+            outbuf[hold] = (unsigned char)(count - 1);
+        }
+    }
+    *outputSizeP = outCurs;
+}
+
+
+
+static void
+setFlagElement(void            * const outP,
+               enum pm_RleMode   const mode,
+               bool              const isRepeatRun,
+               size_t            const count) {
+/*---------------------------------------------------------------------------
+  Write the flag byte or word at specified point in the output buffer.
+-----------------------------------------------------------------------------*/
+    switch (mode) {
+    case PM_RLE_SGI16:
+        * (uint16_t *) outP =  (isRepeatRun ? 0x00 : 0x80 ) | count;
+        break;
+    case PM_RLE_PALM16:
+        * (unsigned char *) outP = isRepeatRun ?
+            (unsigned char)(257 - count) : (unsigned char) (count - 1);
+        break;
+    default:
+        pm_error(errorUndefinedMode, mode);
+    }
+}
+
+
+
+void
+pm_rlenc_compressword(const uint16_t   * const inbuf,
+                      unsigned char *    const outbuf,
+                      enum pm_RleMode    const mode,
+                      size_t             const inSize,
+                      size_t           * const outputSizeP) {
+/*---------------------------------------------------------------------------
+   Similar to pm_rlenc_byte(), but this works with two-byte elements.  The
+   difference between SGI16 and PALM16 is the size of the flag.  SGI16 : 16
+   bits; PALM16 : 8 bits
+
+   'inSize' is a number of words,but *outputSizeP is a number of bytes.
+-----------------------------------------------------------------------------*/
+    size_t inCurs, outCurs;
+    size_t flagSz;
+    unsigned int maxRunSz;
+
+    switch (mode) {
+    case PM_RLE_SGI16:
+        flagSz = 2;
+        maxRunSz = 127;
+        break;
+    case PM_RLE_PALM16:
+        flagSz = 1;
+        maxRunSz = 128;
+        break;
+    default:
+        pm_error(errorUndefinedMode, mode);
+    }
+
+    for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
+        if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
+            uint16_t const runValue = inbuf[inCurs];
+            size_t count;
+            /* Do a run of 'runValue' values */
+            for (count = 0;
+                 count < maxRunSz &&
+                     inCurs < inSize &&
+                     inbuf[inCurs] == runValue;
+                 ++inCurs, ++count)
+                ;
+            setFlagElement(&outbuf[outCurs], mode, TRUE, count);
+            outCurs += flagSz;
+            * (uint16_t *) &outbuf[outCurs] = runValue;
+            outCurs += 2;
+        } else {
+            /*Do a non run*/
+            size_t const nonrunStart = inCurs;
+            size_t count;
+            count = 0;
+            while (count < maxRunSz &&
+                   ((inCurs + 2 >= inSize && inCurs < inSize) ||
+                    (inCurs + 2 <  inSize &&
+                     (inbuf[inCurs] != inbuf[inCurs+1]
+                      || inbuf[inCurs] != inbuf[inCurs+2])))) {
+                ++inCurs;
+                ++count;
+            }
+            setFlagElement(&outbuf[outCurs], mode, FALSE, count);
+            outCurs += flagSz;
+            memcpy(&outbuf[outCurs], &inbuf[nonrunStart], count * 2);
+            outCurs += count * 2;
+        }
+    }
+    
+    if (mode == PM_RLE_SGI16) {
+        * (uint16_t *) &outbuf[outCurs] = 0;     /* terminator */
+        outCurs += 2;
+    }
+
+    *outputSizeP = outCurs;
+}
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   Worst case output size
+
+   The term "worst-case output size" can mean one of two things depending on
+   whether the encoder is efficient or not.
+
+   Sub-optimal encoder: the output size can be up to twice the input size.
+   This happens (or one may rather say "is achieved by a determined encoder")
+   when input is split into one-byte blocks.
+
+   Efficient encoder: the output is larger than the input by the minimum
+   number of flag bytes.  The worst case happens when there are no repeat
+   sequences in the input.
+
+   The key to an efficient encoder is correct handling of short, inefficient
+   sequences.  The following algorithm (not actually used in this file)
+   describes the idea.
+
+   A run is a maximal set of two or more consecutive identical values in the
+   input.  A nonrun is a maximal set of values in which every value is
+   different from its neighbors.
+
+   A compressed block is one that encodes a sequence of identical values
+   (which could be a run or just part of a run) as a value and a count.
+   count.  An uncompressed block is one that encodes a sequence of any values
+   by listing the values individually.
+
+   Start by encoding the entire input as uncompressed blocks.  Seek runs that
+   can be encoded with compressed blocks, but only if doing so doesn't make
+   the output larger.
+
+   Criteria to avoid bloat:
+
+     - Overhead for a single uncompressed block: 1 byte.
+
+     - Overhead for one uncompressed block and a compressed block: 2 bytes.
+
+     - Overhead for two uncompressed blocks and a compressed block: 3 bytes.
+
+     - New blocks at the edge of any existing block add 1 byte of overhead.
+       New blocks in the middle of existing blocks add 2 bytes of overhead.
+
+     - Whenever savings are larger than the added overhead, encode the run
+       as a compressed block.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+
+size_t
+pm_rlenc_maxbytes(size_t          const inSize,  /* number of elements */
+                  enum pm_RleMode const mode) {
+/*---------------------------------------------------------------------------
+   Calculate worst case output size from input size and encoding mode.
+
+   'inSize' counts the number of elements, not bytes: input size is (2 *
+   inSize) bytes if input is an array of 16-bit words.
+
+   Return value is the maximum possible output size in bytes regardless of
+   type of input.
+
+   Abort the program if the maximum possible output size is greater than
+   INT_MAX.
+-----------------------------------------------------------------------------*/
+   /* The upper limit could be raised above INT_MAX, but no program needs that
+      today.
+   */
+    size_t blockSize;
+    size_t flagSize;
+    size_t itemSize;
+    size_t miscSize;
+    size_t overhead;
+
+    switch (mode) {
+    case PM_RLE_PACKBITS:
+        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_SGI8:
+        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_GRAPHON:  case PM_RLE_PPA:
+        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_SGI16:
+        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break;
+    case PM_RLE_PALM16:
+        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break;
+    default:
+        pm_error(errorUndefinedMode, mode);
+    }
+    
+    overhead = miscSize +
+        (inSize / blockSize + (inSize % blockSize > 0 ? 1 : 0) ) * flagSize;
+
+    if (inSize > INT_MAX / itemSize ||
+        inSize * itemSize > INT_MAX - overhead)
+        pm_error("Cannot do RLE compression.  Input too large.");
+
+    return inSize * itemSize + overhead;
+}
+
+
+
+void
+pm_rlenc_allocoutbuf(unsigned char ** const outbufP,
+                     size_t           const inSize,  /* element count */
+                     enum pm_RleMode  const mode) {
+/*---------------------------------------------------------------------------
+   Allocate an output buffer sufficient for input with inSize elements, using
+   compression mode 'mode'.  Element may be byte or word, whichever 'mode'
+   implies.
+-----------------------------------------------------------------------------*/
+    size_t const size = pm_rlenc_maxbytes(inSize, mode);
+
+    unsigned char * outbuf;
+
+    MALLOCARRAY(outbuf, size);
+    if (outbuf == NULL)
+        pm_error("Out of memory trying to get %u bytes for RLE output buffer",
+                 (unsigned)size);
+
+    *outbufP = outbuf;
+}
+
+
+
+void
+pm_rlenc_freebuf(void * const buf) {
+    free(buf);
+}
+
+
diff --git a/lib/util/runlength.h b/lib/util/runlength.h
new file mode 100644
index 00000000..4857ae61
--- /dev/null
+++ b/lib/util/runlength.h
@@ -0,0 +1,51 @@
+#ifndef RUNLENGTH_INCLUDED
+#define RUNLENGTH_INCLUDED
+
+#include "pm_config.h"
+
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* to fake out automatic code indenters */
+#endif
+
+
+enum pm_RleMode { PM_RLE_PACKBITS,          /* most common mode */
+                  PM_RLE_GRAPHON,           /* reserved */ 
+                  PM_RLE_PPA,               /* reserved */
+                  PM_RLE_SGI8,              /* reserved */
+                  PM_RLE_SGI16,
+                  PM_RLE_PALM16
+                };
+
+size_t
+pm_rlenc_maxbytes (size_t          const inSize,
+                   enum pm_RleMode const mode);
+
+void
+pm_rlenc_allocoutbuf(unsigned char ** const outbufP,
+                     size_t           const inSize,
+                     enum pm_RleMode  const mode);
+
+
+void
+pm_rlenc_freebuf(void * const buf);
+
+void
+pm_rlenc_compressbyte(const unsigned char * const inbuf,
+                      unsigned char       * const outbuf,
+                      enum pm_RleMode       const mode,
+                      size_t                const inSize,
+                      size_t              * const outputSizeP);
+
+void
+pm_rlenc_compressword(const uint16_t      * const inbuf,
+                      unsigned char       * const outbuf,
+                      enum pm_RleMode       const mode,
+                      size_t                const itemCnt,
+                      size_t              * const outputSizeP);
+
+#endif
diff --git a/other/Makefile b/other/Makefile
index 5acd7578..bd2c9dc2 100644
--- a/other/Makefile
+++ b/other/Makefile
@@ -25,7 +25,7 @@ endif
 
 PORTBINARIES = pamarith pambayer pamchannel pamdepth \
 	pamendian pamexec pamfix pamlookup pampick pamsplit \
-	pamstack pamsummcol pamvalidate pnmcolormap \
+	pamstack pamsummcol pamunlookup pamvalidate pnmcolormap \
 	ppmdcfont ppmddumpfont ppmdmkfont 
 
 ifneq ($(LINUXSVGALIB),NONE)
diff --git a/other/pamlookup.c b/other/pamlookup.c
index d5f046a5..4ceb047f 100644
--- a/other/pamlookup.c
+++ b/other/pamlookup.c
@@ -13,32 +13,36 @@
 
 ============================================================================*/
 
+#include <assert.h>
+
 #include "pm_c_util.h"
-#include "pam.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "pm_system.h"
 #include "nstring.h"
+#include "pam.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *indexFilespec;  
-    char *lookupFilespec;
-    char *missingcolor;  /* -missingcolor value.  null if not specified */
-    unsigned int fit;  /* -fit option */
+    const char * indexFilespec;  
+    char *       lookupFilespec;
+    char *       missingcolor;  /* null if not specified */
+    unsigned int fit;
+    unsigned int byplane;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
     optStruct3 opt;
@@ -47,6 +51,8 @@ parseCommandLine(int argc, char ** const argv,
     
     unsigned int lookupfileSpec, missingcolorSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "lookupfile",     OPT_STRING, &cmdlineP->lookupFilespec,  
             &lookupfileSpec, 0);
@@ -54,12 +60,14 @@ parseCommandLine(int argc, char ** const argv,
             &cmdlineP->missingcolor,   &missingcolorSpec, 0);
     OPTENT3(0,   "fit", OPT_FLAG, 
             NULL,   &cmdlineP->fit, 0);
+    OPTENT3(0,   "byplane", OPT_FLAG, 
+            NULL,   &cmdlineP->byplane, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!lookupfileSpec)
@@ -68,10 +76,15 @@ parseCommandLine(int argc, char ** const argv,
     if (!missingcolorSpec)
         cmdlineP->missingcolor = NULL;
 
+    if (cmdlineP->byplane && cmdlineP->missingcolor)
+        pm_error("You cannot specify -missingcolor with -byplane");
+
     if (argc-1 < 1)
         cmdlineP->indexFilespec = "-";
     else
         cmdlineP->indexFilespec = argv[1];
+
+    free(option_def);
 }        
 
 
@@ -111,19 +124,32 @@ fitLookup(tuple **     const inputLookup,
 
 
 static void
-getLookup(const char * const lookupFilespec, 
+getLookup(const char * const lookupFileName, 
           unsigned int const indexDegree,
           unsigned int const indexMaxval,
           tuple ***    const lookupP,
           struct pam * const lookuppamP,
           bool         const fit) {
+/*----------------------------------------------------------------------------
+   Get the lookup image (the one that maps integers to tuples, e.g. a
+   color index / color map / palette) from the file named 
+   'lookupFileName'.
 
-    FILE*  lookupfileP;
+   Interpret the lookup image for use with indices that are ntuples of size
+   'indexDegree' (normally 1, could be 2) whose elements range from 0 through
+   'indexMaxval'
+
+   Iff 'fit' is true, stretch or compress the image in the file to fit
+   exactly the range identified by 'indexMaxval'.
+
+   Return the image as *lookupP and *lookuppamP.
+-----------------------------------------------------------------------------*/
+    FILE *  lookupfileP;
 
     struct pam inputLookuppam;
-    tuple** inputLookup;
+    tuple ** inputLookup;
 
-    lookupfileP = pm_openr(lookupFilespec);
+    lookupfileP = pm_openr(lookupFileName);
     inputLookup = pnm_readpam(lookupfileP, 
                               &inputLookuppam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -153,14 +179,14 @@ getLookup(const char * const lookupFilespec,
     if (indexDegree == 2 && lookuppamP->height - 1 > indexMaxval)
         pm_message("Warning: your lookup table image is taller than "
                    "the maxval of "
-                   "your index message, so the bottom end of the lookup "
+                   "your index image, so the bottom end of the lookup "
                    "table image has no effect on the output.");
 }
 
 
 
 static void
-computeDefaultTuple(struct cmdlineInfo const cmdline, 
+computeDefaultTuple(struct CmdlineInfo const cmdline, 
                     tuple **           const lookup,
                     struct pam *       const lookuppamP, 
                     tuple *            const defaultTupleP) {
@@ -203,20 +229,94 @@ computeDefaultTuple(struct cmdlineInfo const cmdline,
 
 
 static void
-doLookup(struct pam const indexpam,
-         struct pam const outpamarg,
-         tuple      const defaultTuple,
-         tuple **   const lookup,
-         struct pam const lookuppam) {
+doLookupByPlane(struct pam const indexpam,
+                tuple **   const lookup,
+                struct pam const lookuppam,
+                FILE *     const ofP) {
+/*----------------------------------------------------------------------------
+   Write an output image to *ofP derived from the input image read per
+   'indexpam' (now positioned to its raster).
 
+   Base each tuple of the output on the tuple in the same place in the input.
+   Look up each sample of the input tuple in the lookupt image given by
+   'lookup' and 'lookuppam' to get the corresponding sample of the output
+   image.
+
+   Our output image has the same width, height, depth, image type, and tuple
+   type as the input image and the same maxval as the lookup image.
+
+   We ignore any plane or row after the first in the lookup image.  We expect
+   its width to match the maxval of the input image.
+-----------------------------------------------------------------------------*/
     struct pam outpam;
     unsigned int row;
 
     tuple* tuplerowIndex;
     tuple* tuplerowOut;
 
-    outpam = outpamarg;
+    outpam = indexpam;  /* initial value */
+    outpam.maxval = lookuppam.maxval;
+    outpam.file = ofP;
+    
+    tuplerowIndex = pnm_allocpamrow(&indexpam);
+    tuplerowOut = pnm_allocpamrow(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    assert(lookuppam.width == indexpam.maxval + 1);
+        /* Calling condition */
+
+    for (row = 0; row < indexpam.height; ++row) {
+        unsigned int col;
+        pnm_readpamrow(&indexpam, tuplerowIndex);
+        
+        for (col = 0; col < indexpam.width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < indexpam.depth; ++plane) {
+                unsigned int const index = tuplerowIndex[col][plane];
+
+                if (index > lookuppam.maxval)
+                    pm_error("Sample value %u in the lookup image exceeds "
+                             "the lookup image's maxval (%u)",
+                             index, (unsigned)lookuppam.maxval);
+
+                tuplerowOut[col][plane] = lookup[0][index][0];
+            }
+        }
+        pnm_writepamrow(&outpam, tuplerowOut);
+    }
+    pnm_freepamrow(tuplerowIndex);
+    pnm_freepamrow(tuplerowOut);
+}
+
+
+
+static void
+doLookupWholeTuple(struct pam const indexpam,
+                   tuple      const defaultTuple,
+                   tuple **   const lookup,
+                   struct pam const lookuppam,
+                   FILE *     const ofP) {
+/*----------------------------------------------------------------------------
+   Write an output image to *ofP derived from the input image read per
+   'indexpam' (now positioned to its raster).
+
+   For each tuple of the output, use the corresponding tuple of the input as
+   an index into the lookup image given by 'lookup' and 'lookuppam'.  If that
+   index is not present in the lookup image, put 'defaultTuple' in the output.
+-----------------------------------------------------------------------------*/
+    struct pam outpam;
+    unsigned int row;
+
+    tuple* tuplerowIndex;
+    tuple* tuplerowOut;
 
+    outpam = lookuppam;  /* initial value */
+    outpam.height = indexpam.height;
+    outpam.width = indexpam.width;
+    outpam.file = ofP;
+    
     tuplerowIndex = pnm_allocpamrow(&indexpam);
     tuplerowOut = pnm_allocpamrow(&outpam);
 
@@ -254,18 +354,18 @@ doLookup(struct pam const indexpam,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** const argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam indexpam;
-    struct pam outpam;
-    FILE*  ifP;
+    FILE * ifP;
+    unsigned int indexDegree;
     struct pam lookuppam;
-    tuple** lookup;
+    tuple ** lookup;
 
     tuple defaultTuple;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -273,28 +373,30 @@ main(int argc, char *argv[]) {
 
     pnm_readpaminit(ifP, &indexpam, PAM_STRUCT_SIZE(tuple_type));
 
-    if (indexpam.depth != 1 && indexpam.depth != 2)
-        pm_error("The input (index) file must have depth 1 or 2.  "
-                 "Yours has depth %d",
+    if (!cmdline.byplane && (indexpam.depth != 1 && indexpam.depth != 2))
+        pm_error("Unless you specify -byplane, "
+                 "the input (index) file must have depth 1 or 2.  "
+                 "Yours has depth %u",
                  indexpam.depth);
 
-    getLookup(cmdline.lookupFilespec, indexpam.depth, indexpam.maxval, 
-              &lookup, &lookuppam, cmdline.fit);
+    indexDegree = cmdline.byplane ? 1 : indexpam.depth;
+
+    getLookup(cmdline.lookupFilespec, indexDegree, indexpam.maxval, 
+              &lookup, &lookuppam, cmdline.fit || cmdline.byplane);
 
     computeDefaultTuple(cmdline, lookup, &lookuppam, &defaultTuple);
 
-    outpam = lookuppam;
-    outpam.height = indexpam.height;
-    outpam.width = indexpam.width;
-    outpam.file = stdout;
-    
-    doLookup(indexpam, outpam, defaultTuple, lookup, lookuppam);
+    if (cmdline.byplane)
+        doLookupByPlane(indexpam, lookup, lookuppam, stdout);
+    else
+        doLookupWholeTuple(indexpam, defaultTuple, lookup, lookuppam, stdout);
 
     pm_close(ifP);
 
     pnm_freepamtuple(defaultTuple);
     pnm_freepamarray(lookup, &lookuppam);
     
-    exit(0);
+    return 0;
 }
 
+
diff --git a/other/pamunlookup.c b/other/pamunlookup.c
new file mode 100644
index 00000000..77c2807b
--- /dev/null
+++ b/other/pamunlookup.c
@@ -0,0 +1,250 @@
+/*============================================================================
+                               pamunlookup
+==============================================================================
+  Find tuple values from an input image in a lookup table and
+  produce a corresponding index image containing table indices.
+
+  The lookup table is a one-row PAM image tuple type the same as the input
+  image.  The output index image has the same width and height as the input
+  image depth 1, and maxval equal to the width of the lookup image (the
+  possible values include one for each column in the lookup image, plus one
+  for tuple values that are not in the lookup image).
+
+  By Bryan Henderson, San Jose CA 2015.08.08
+
+============================================================================*/
+
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm_system.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    char *       lookupfile;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    
+    unsigned int lookupfileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "lookupfile",     OPT_STRING, &cmdlineP->lookupfile,  
+            &lookupfileSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!lookupfileSpec)
+        pm_error("You must specify the -lookupfile option");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}        
+
+
+
+static void
+getLookup(const char * const lookupFileName, 
+          tuple ***    const lookupP,
+          struct pam * const lookuppamP) {
+/*----------------------------------------------------------------------------
+   Get the lookup image (the one that maps integers to tuples, e.g. a
+   color index / color map / palette) from the file named 
+   'lookupFileName'.
+
+   Return the image as *lookupP and *lookuppamP.
+-----------------------------------------------------------------------------*/
+    FILE *  lookupfileP;
+
+    struct pam inputLookuppam;
+    tuple ** inputLookup;
+
+    lookupfileP = pm_openr(lookupFileName);
+    inputLookup = pnm_readpam(lookupfileP, 
+                              &inputLookuppam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_close(lookupfileP);
+    
+    if (inputLookuppam.height != 1)
+        pm_error("The lookup table image must be one row.  "
+                 "Yours is %u rows.", 
+                 inputLookuppam.height);
+
+    *lookupP = inputLookup;
+    *lookuppamP = inputLookuppam;
+}
+
+
+
+static void
+makeReverseLookupHash(struct pam * const lookuppamP,
+                      tuple **     const lookup,
+                      tuplehash *  const hashP) {
+/*----------------------------------------------------------------------------
+   Create a tuple hash that maps each tuple values in the first row of
+   'lookup' to the number of the column in which it appears.
+
+   Abort the program with an error if the same tuple value occurs in two
+   columns of the first row.
+-----------------------------------------------------------------------------*/
+    tuplehash hash;
+    unsigned int col;
+
+    hash = pnm_createtuplehash();
+
+    for (col = 0; col < lookuppamP->width; ++col) {
+        tuple const thisValue = lookup[0][col];
+        
+        int found;
+        int priorValue;
+
+        pnm_lookuptuple(lookuppamP, hash, thisValue, &found, &priorValue);
+
+        if (found)
+            pm_error("Same tuple value occurs in both Column %u and "
+                     "Column %u of the lookup image", priorValue, col);
+        else {
+            int fits;
+            pnm_addtotuplehash(lookuppamP, hash, lookup[0][col], col, &fits);
+
+            if (!fits)
+                pm_error("Out of memory constructing hash of lookup table");
+        }
+    }
+
+    *hashP = hash;
+}
+
+
+
+static void
+doUnlookup(struct pam * const inpamP,
+           tuplehash    const lookupHash,
+           sample       const maxIndex,
+           FILE *       const ofP) {
+
+    struct pam outpam;
+    unsigned int row;
+    tuple * inrow;
+    tuple * outrow;
+
+    inrow = pnm_allocpamrow(inpamP);
+
+    outpam.size = sizeof(outpam);
+    outpam.len = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file = ofP;
+    outpam.format = PAM_FORMAT;
+    outpam.height = inpamP->height;
+    outpam.width = inpamP->width;
+    outpam.depth = 1;
+    outpam.maxval = maxIndex + 1;  /* +1 for missing color */
+    strcpy(outpam.tuple_type, "INDEX");
+
+    pnm_writepaminit(&outpam);
+
+    outrow = pnm_allocpamrow(&outpam);
+
+    for (row = 0; row < inpamP->height; ++row) {
+        unsigned int col;
+        
+        pnm_readpamrow(inpamP, inrow);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            int found;
+            int index;
+            pnm_lookuptuple(inpamP, lookupHash, inrow[col], &found, &index);
+
+            if (found) {
+                assert(index <= outpam.maxval);
+                outrow[col][0] = index;
+            } else
+                outrow[col][0] = maxIndex + 1;
+        }
+        pnm_writepamrow(&outpam, outrow);
+    }
+
+    pnm_freepamrow(outrow);
+    pnm_freepamrow(inrow);
+}
+
+
+
+int
+main(int argc, const char ** const argv) {
+
+    struct CmdlineInfo cmdline;
+    struct pam inpam;
+    FILE * ifP;
+    struct pam lookuppam;
+    tuple ** lookup;
+
+    tuplehash lookupHash;
+    
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    getLookup(cmdline.lookupfile, &lookup, &lookuppam);
+
+    if (inpam.depth != lookuppam.depth)
+        pm_error("The lookup image has depth %u, but the input image "
+                 "has depth %u.  They must be the same",
+                 lookuppam.depth, inpam.depth);
+    if (!streq(inpam.tuple_type, lookuppam.tuple_type))
+        pm_error("The lookup image has tupel type '%s', "
+                 "but the input image "
+                 "has tuple type '%s'.  They must be the same",
+                 lookuppam.tuple_type, inpam.tuple_type);
+
+    makeReverseLookupHash(&lookuppam, lookup, &lookupHash);
+
+    doUnlookup(&inpam, lookupHash, lookuppam.width-1, stdout);
+
+    pm_close(ifP);
+
+    pnm_destroytuplehash(lookupHash);
+    pnm_freepamarray(lookup, &lookuppam);
+    
+    return 0;
+}
+
+
diff --git a/test/Test-Order b/test/Test-Order
index d21b9e8a..92efca05 100644
--- a/test/Test-Order
+++ b/test/Test-Order
@@ -83,6 +83,8 @@ pbmtog3.test
 411toppm.test
 eyuvtoppm.test
 
+pbm-misc-converters.test
+
 # Miscellaneous utility tests
 
 ppmdfont.test
@@ -100,6 +102,7 @@ rgb3-roundtrip.test
 ppmchange-roundtrip.test
 pamdice-roundtrip.test
 pamslice-roundtrip.test
+lookup-roundtrip.test
 
 # Round-trip tests : lossless converters
 
@@ -129,11 +132,13 @@ pfm-roundtrip.test
 pi3-roundtrip.test
 pict-roundtrip.test
 png-roundtrip.test
+png-roundtrip2.test
 ps-roundtrip.test
 ps-alt-roundtrip.test
 sgi-roundtrip.test
 sbig-roundtrip.test
 st4-roundtrip.test
+sunicon-roundtrip.test
 sunrast-roundtrip.test
 targa-roundtrip.test
 tiff-roundtrip.test
diff --git a/test/all-in-place.ok b/test/all-in-place.ok
index c58eb024..6ba75993 100644
--- a/test/all-in-place.ok
+++ b/test/all-in-place.ok
@@ -66,7 +66,6 @@ pamperspective: ok
 pampick: ok
 pampop9: ok
 pamrecolor: ok
-pamrgbatopng: ok
 pamrubber: ok
 pamscale: ok
 pamseq: ok
@@ -95,6 +94,7 @@ pamtooctaveimg: ok
 pamtopam: ok
 pamtopdbimg: ok
 pamtopfm: ok
+pamtopng: ok
 pamtopnm: ok
 pamtosrf: ok
 pamtosvg: ok
@@ -104,6 +104,7 @@ pamtouil: ok
 pamtowinicon: ok
 pamtoxvmini: ok
 pamundice: ok
+pamunlookup: ok
 pamvalidate: ok
 pamwipeout: ok
 pamx: ok
diff --git a/test/all-in-place.test b/test/all-in-place.test
index 79459f7a..4c8af56b 100755
--- a/test/all-in-place.test
+++ b/test/all-in-place.test
@@ -105,7 +105,6 @@ ordinary_testprogs="\
   pampick \
   pampop9 \
   pamrecolor \
-  pamrgbatopng \
   pamrubber \
   pamscale \
   pamseq \
@@ -134,6 +133,7 @@ ordinary_testprogs="\
   pamtopam \
   pamtopdbimg \
   pamtopfm \
+  pamtopng \
   pamtopnm \
   pamtosrf \
   pamtosvg \
@@ -143,6 +143,7 @@ ordinary_testprogs="\
   pamtowinicon \
   pamtoxvmini \
   pamundice \
+  pamunlookup \
   pamvalidate \
   pamwipeout \
   pamx \
diff --git a/test/ilbm-roundtrip.ok b/test/ilbm-roundtrip.ok
index cd357916..54574a18 100644
--- a/test/ilbm-roundtrip.ok
+++ b/test/ilbm-roundtrip.ok
@@ -1,2 +1,10 @@
+829921912 685
+829921912 685
+829921912 685
+829921912 685
+1926073387 101484
+1926073387 101484
 1926073387 101484
 984199586 101484
+2059976475 661
+2059976475 661
diff --git a/test/ilbm-roundtrip.test b/test/ilbm-roundtrip.test
index a13fc8be..f62368ff 100755
--- a/test/ilbm-roundtrip.test
+++ b/test/ilbm-roundtrip.test
@@ -2,10 +2,27 @@
 # This script tests: ppmtoilbm ilbmtoppm
 # Also requires: pamseq pamdepth pamtopnm pnmremap
 
+#Test. 1  Should produce 829921912 685 four times
+#Output is PPM raw, 14 by 16  maxval 255
+ppmtoilbm testgrid.pbm | ilbmtoppm | cksum
+ppmtoilbm -aga testgrid.pbm | ilbmtoppm | cksum
+ppmtoilbm -ham6 testgrid.pbm | ilbmtoppm | cksum
+ppmtoilbm -ham8 testgrid.pbm | ilbmtoppm | cksum
 
-#Test.  Should produce 1926073387 101484
+
+#Test. 2  Should produce 1926073387 101484 three times
 ppmtoilbm testimg.ppm | ilbmtoppm | cksum
+ppmtoilbm -24force testimg.ppm | ilbmtoppm | cksum
+ppmtoilbm -dcbits 8 8 8 -nocompress testimg.ppm | ilbmtoppm | cksum
+
 
-#Test.  Should print 984199586 101484
+#Test. 3  Should print 984199586 101484
 pamseq 3 5 -tupletype=RGB | pamdepth 255 | pamtopnm | \
   pnmremap -mapfile=- testimg.ppm | ppmtoilbm | ilbmtoppm | cksum
+
+
+#Test. 4 Should print 2059976475 661 twice
+pamseq 3 5 -tupletype=RGB | pamtopnm | \
+  ppmtoilbm -compress | ilbmtoppm | cksum
+pamseq 3 5 -tupletype=RGB | pamtopnm | \
+  ppmtoilbm -nocompress | ilbmtoppm | cksum
diff --git a/test/legacy-names.ok b/test/legacy-names.ok
index 57343afa..9676639b 100644
--- a/test/legacy-names.ok
+++ b/test/legacy-names.ok
@@ -2,6 +2,7 @@ bmptoppm: ok
 gemtopbm: ok
 icontopbm: ok
 pamfixtrunc: ok
+pamrgbatopng: ok
 pbmtoicon: ok
 pgmedge: ok
 pgmnorm: ok
diff --git a/test/legacy-names.test b/test/legacy-names.test
index 9940f207..df40e62d 100755
--- a/test/legacy-names.test
+++ b/test/legacy-names.test
@@ -53,6 +53,7 @@ ordinary_testprogs="\
   gemtopbm \
   icontopbm \
   pamfixtrunc \
+  pamrgbatopng \
   pbmtoicon \
   pgmedge \
   pgmnorm \
diff --git a/test/lookup-roundtrip.ok b/test/lookup-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/lookup-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/lookup-roundtrip.test b/test/lookup-roundtrip.test
new file mode 100755
index 00000000..63ec0777
--- /dev/null
+++ b/test/lookup-roundtrip.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pamlookup pamunlookup
+# Also requires: ppmhist
+
+tmpdir=${tmpdir:-/tmp}
+mapfile=${tmpdir}/mapfile
+
+ppmhist testimg.ppm -map > ${mapfile}
+
+# Test.  Should produce 1926073387 101484
+pamunlookup -lookupfile=${mapfile} testimg.ppm |\
+  pamlookup -lookupfile=${mapfile} | cksum
diff --git a/test/pbm-misc-converters.ok b/test/pbm-misc-converters.ok
new file mode 100644
index 00000000..a137102f
--- /dev/null
+++ b/test/pbm-misc-converters.ok
@@ -0,0 +1,27 @@
+1638343024 43
+2141128209 77
+2542756600 120
+3102495729 32
+2414506375 47
+3241517214 145
+1454090165 46
+1436169407 46
+1454090165 46
+2912484298 46
+3576177652 52
+1478164284 52
+3213223606 141
+3213223606 141
+3213223606 141
+1463148415 108
+203901789 30
+3732005859 92
+2459345477 86
+424535246 92
+609530223 252
+4195053594 248
+2602382240 43
+129620534 361
+2256096096 80
+1349121911 149
+3955750161 284701
diff --git a/test/pbm-misc-converters.test b/test/pbm-misc-converters.test
new file mode 100755
index 00000000..aa71489b
--- /dev/null
+++ b/test/pbm-misc-converters.test
@@ -0,0 +1,40 @@
+#! /bin/bash
+# This script tests: pbmto10x pbmto4425 pbmtoascii pbmtobbnbg
+# This script tests: pbmtodjvurle pbmtoepson pbmtogo pbmtoibm23xx
+# This script tests: pbmtolj pbmtoln03 pbmtomatrixorbital pbmtonokia
+# This script tests: pbmtoplot pbmtoptx pbmtozinc
+# Also requires: pbmpage
+
+# Note: one-way test.
+# These converters do not have counterparts that work in the opposite
+# direction.  We check whether the output is unchanged from older
+# versions.
+
+pbmto10x             testgrid.pbm | cksum
+pbmto4425            testgrid.pbm | cksum
+pbmtoascii           testgrid.pbm | cksum
+pbmtoascii -2x4      testgrid.pbm | cksum
+pbmtobbnbg         < testgrid.pbm | cksum
+pbmtodjvurle         testgrid.pbm | cksum
+pbmtoepson           testgrid.pbm | cksum
+pbmtoepson -protocol=escp   testgrid.pbm | cksum
+pbmtoepson -protocol=escp9  testgrid.pbm | cksum
+pbmtoepson -nonadjacent     testgrid.pbm | cksum
+pbmtogo              testgrid.pbm | cksum
+pbmtoibm23xx -xres=60 -yres=60 testgrid.pbm | cksum
+pbmtolj              testgrid.pbm | cksum
+pbmtolj  -packbits   testgrid.pbm | cksum
+pbmtolj  -compress   testgrid.pbm | cksum
+pbmtoln03            testgrid.pbm | cksum
+pbmtomatrixorbital < testgrid.pbm | cksum
+pbmtonokia -fmt HEX_NOL testgrid.pbm | cksum
+pbmtonokia -fmt HEX_NGG testgrid.pbm | cksum
+pbmtonokia -fmt HEX_NPM testgrid.pbm | cksum
+pbmtonokia -fmt NOL  testgrid.pbm | cksum
+pbmtonokia -fmt NGG  testgrid.pbm | cksum
+pbmtonokia -fmt NPM  testgrid.pbm | cksum
+pbmtoplot            testgrid.pbm | cksum
+pbmtoptx             testgrid.pbm | cksum
+pbmtozinc            testgrid.pbm | cksum
+
+(pbmpage 1; pbmpage 2; pbmpage 3) | pbmtoppa | cksum
diff --git a/test/png-roundtrip.test b/test/png-roundtrip.test
index e70f6765..7e7c4a9b 100755
--- a/test/png-roundtrip.test
+++ b/test/png-roundtrip.test
@@ -2,6 +2,9 @@
 # This script tests: pngtopam pnmtopng
 # Also requires:
 
+# Failure message
+## If this test fails and pnm-roundtrip2.test succeeds, it indicates
+## some problem with pnmtopng.
 
 # Test 1.  Should print 1926073387 101484 18 times
 for flags in "" -interlace \
diff --git a/test/png-roundtrip2.ok b/test/png-roundtrip2.ok
new file mode 100644
index 00000000..0e712ce7
--- /dev/null
+++ b/test/png-roundtrip2.ok
@@ -0,0 +1,4 @@
+1926073387 101484
+1926073387 101484
+2425386270 41
+2425386270 41
diff --git a/test/png-roundtrip2.test b/test/png-roundtrip2.test
new file mode 100755
index 00000000..af2ad029
--- /dev/null
+++ b/test/png-roundtrip2.test
@@ -0,0 +1,22 @@
+#! /bin/bash
+# This script tests: pngtopam pamtopng
+# Also requires:
+
+# Failure message
+## If this test fails and pnm-roundtrip.test succeeds, it indicates
+## some problem with pamtopng.
+##
+## If both tests fail, the likely cause is a problem with pngtopam.
+## It is also possible that there is some problem in libpng.
+
+# Test 1.  Should print 1926073387 101484 twice
+for flags in "" -gamma=.45
+  do
+  pamtopng testimg.ppm $flags | pngtopam | cksum
+  done
+
+# Test 2.  Should print 2425386270 41 twice
+for flags in "" -gamma=.45
+  do
+  pamtopng testgrid.pbm $flags | pngtopam | cksum
+  done
diff --git a/test/sgi-roundtrip.ok b/test/sgi-roundtrip.ok
index 541d59b1..b1bd5ba6 100644
--- a/test/sgi-roundtrip.ok
+++ b/test/sgi-roundtrip.ok
@@ -4,3 +4,5 @@
 1926073387 101484
 538848130 235
 538848130 235
+2394972481 463
+2394972481 463
diff --git a/test/sgi-roundtrip.test b/test/sgi-roundtrip.test
index 5012aab7..88efb75e 100755
--- a/test/sgi-roundtrip.test
+++ b/test/sgi-roundtrip.test
@@ -32,3 +32,12 @@ rm ${b_sgi} ${b_red} ${b_grn} ${b_blu}
 # Test 3.  Should produce 2425386270 41 twice
 pnmtosgi testgrid.pbm | sgitopnm | cksum             # Defaults to -rle
 pnmtosgi -verbatim testgrid.pbm | sgitopnm | cksum
+
+
+testgrid_pgm=${tmpdir}/testgrid.pgm
+
+# Test 4. Should produce 2394972481 463 twice
+pamdepth 65535 testgrid.pbm | pamtopnm | tee ${testgrid_pgm} | \
+  pnmtosgi -rle | sgitopnm | cksum
+pnmtosgi -verbatim ${testgrid_pgm} | sgitopnm | cksum
+rm ${testgrid_pgm}
diff --git a/test/sunicon-roundtrip.ok b/test/sunicon-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/sunicon-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/sunicon-roundtrip.test b/test/sunicon-roundtrip.test
new file mode 100755
index 00000000..a52fda68
--- /dev/null
+++ b/test/sunicon-roundtrip.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: pbmtosunicon sunicontopnm
+# Also requires: pamcut
+
+# Width of Sun icons are multiples of 8.
+
+# Test.  Should print: 2425386270 41
+pbmtosunicon testgrid.pbm | sunicontopnm | pamcut 1 0 14 16 | cksum
diff --git a/urt/rle_getrow.c b/urt/rle_getrow.c
index bd7d1c8e..679811cc 100644
--- a/urt/rle_getrow.c
+++ b/urt/rle_getrow.c
@@ -52,27 +52,26 @@
 
 static int     debug_f;     /* If non-zero, print debug info. */
 
-/*****************************************************************
- * TAG( rle_get_setup )
- * 
- * Read the initialization information from an RLE file.
- * Inputs:
- *  the_hdr:    Contains pointer to the input file.
- * Outputs:
- *  the_hdr:    Initialized with information from the
- *          input file.
- *  Returns 0 on success, -1 if the file is not an RLE file,
- *  -2 if malloc of the color map failed, -3 if an immediate EOF
- *  is hit (empty input file), and -4 if an EOF is encountered reading
- *  the setup information.
- * Assumptions:
- *  infile points to the "magic" number in an RLE file (usually
- * byte 0 in the file).
- * Algorithm:
- *  Read in the setup info and fill in the_hdr.
- */
 int
 rle_get_setup(rle_hdr * const the_hdr) {
+/*-----------------------------------------------------------------------------
+  Read the initialization information from an RLE file.
+  Inputs:
+    the_hdr:    Contains pointer to the input file.
+  Outputs:
+    the_hdr:    Initialized with information from the input file.
+  Returns
+     0  on success,
+     -1 if the file is not an RLE file,
+     -2 if malloc of the color map failed,
+     -3 if an immediate EOF is hit (empty input file)
+     -4 if an EOF is encountered reading the setup information.
+  Assumptions:
+    infile points to the "magic" number in an RLE file (usually  byte 0
+    in the file).
+  Algorithm:
+    Read in the setup info and fill in the_hdr.
+---------------------------------------------------------------------------- */
     struct XtndRsetup setup;
     short magic;
     FILE * infile = the_hdr->rle_file;
@@ -80,116 +79,106 @@ rle_get_setup(rle_hdr * const the_hdr) {
     char * comment_buf;
     
     /* Clear old stuff out of the header. */
-    rle_hdr_clear( the_hdr );
-    if ( the_hdr->is_init != RLE_INIT_MAGIC )
-        rle_names( the_hdr, "Urt", "some file", 0 );
-    the_hdr->img_num++;     /* Count images. */
+    rle_hdr_clear(the_hdr);
+    if (the_hdr->is_init != RLE_INIT_MAGIC)
+        rle_names(the_hdr, "Urt", "some file", 0);
+    ++the_hdr->img_num;     /* Count images. */
 
-    VAXSHORT( magic, infile );
-    if ( feof( infile ) )
+    VAXSHORT(magic, infile);
+    if (feof(infile))
         return RLE_EMPTY;
-    if ( magic != RLE_MAGIC )
+    if (magic != RLE_MAGIC)
         return RLE_NOT_RLE;
-    fread( &setup, 1, SETUPSIZE, infile );  /* assume VAX packing */
-    if ( feof( infile ) )
+    fread(&setup, 1, SETUPSIZE, infile);  /* assume VAX packing */
+    if (feof( infile))
         return RLE_EOF;
 
     /* Extract information from setup */
     the_hdr->ncolors = setup.h_ncolors;
-    for ( i = 0; i < the_hdr->ncolors; i++ )
-        RLE_SET_BIT( *the_hdr, i );
+    for (i = 0; i < the_hdr->ncolors; ++i)
+        RLE_SET_BIT(*the_hdr, i);
 
-    if ( !(setup.h_flags & H_NO_BACKGROUND) && setup.h_ncolors > 0 )
-    {
+    if (!(setup.h_flags & H_NO_BACKGROUND) && setup.h_ncolors > 0) {
         rle_pixel * bg_color;
 
         MALLOCARRAY(the_hdr->bg_color, setup.h_ncolors);
         MALLOCARRAY(bg_color, 1 + (setup.h_ncolors / 2) * 2);
-        RLE_CHECK_ALLOC( the_hdr->cmd, the_hdr->bg_color && bg_color,
-                         "background color" );
-        fread( (char *)bg_color, 1, 1 + (setup.h_ncolors / 2) * 2, infile );
-        for ( i = 0; i < setup.h_ncolors; i++ )
+        RLE_CHECK_ALLOC(the_hdr->cmd, the_hdr->bg_color && bg_color,
+                        "background color" );
+        fread((char *)bg_color, 1, 1 + (setup.h_ncolors / 2) * 2, infile);
+        for (i = 0; i < setup.h_ncolors; ++i)
             the_hdr->bg_color[i] = bg_color[i];
-        free( bg_color );
-    }
-    else
-    {
-        (void)getc( infile );   /* skip filler byte */
+        free(bg_color);
+    } else {
+        getc(infile);   /* skip filler byte */
         the_hdr->bg_color = NULL;
     }
 
-    if ( setup.h_flags & H_NO_BACKGROUND )
+    if (setup.h_flags & H_NO_BACKGROUND)
         the_hdr->background = 0;
-    else if ( setup.h_flags & H_CLEARFIRST )
+    else if (setup.h_flags & H_CLEARFIRST)
         the_hdr->background = 2;
     else
         the_hdr->background = 1;
-    if ( setup.h_flags & H_ALPHA )
-    {
+    if (setup.h_flags & H_ALPHA) {
         the_hdr->alpha = 1;
         RLE_SET_BIT( *the_hdr, RLE_ALPHA );
-    }
-    else
+    } else
         the_hdr->alpha = 0;
 
-    the_hdr->xmin = vax_gshort( setup.hc_xpos );
-    the_hdr->ymin = vax_gshort( setup.hc_ypos );
-    the_hdr->xmax = the_hdr->xmin + vax_gshort( setup.hc_xlen ) - 1;
-    the_hdr->ymax = the_hdr->ymin + vax_gshort( setup.hc_ylen ) - 1;
+    the_hdr->xmin = vax_gshort(setup.hc_xpos);
+    the_hdr->ymin = vax_gshort(setup.hc_ypos);
+    the_hdr->xmax = the_hdr->xmin + vax_gshort(setup.hc_xlen) - 1;
+    the_hdr->ymax = the_hdr->ymin + vax_gshort(setup.hc_ylen) - 1;
 
     the_hdr->ncmap = setup.h_ncmap;
     the_hdr->cmaplen = setup.h_cmaplen;
-    if ( the_hdr->ncmap > 0 )
-    {
+    if (the_hdr->ncmap > 0) {
         int const maplen = the_hdr->ncmap * (1 << the_hdr->cmaplen);
+
         int i;
         char *maptemp;
 
         MALLOCARRAY(the_hdr->cmap, maplen);
         MALLOCARRAY(maptemp, 2 * maplen);
-        if ( the_hdr->cmap == NULL || maptemp == NULL )
-        {
+        if (the_hdr->cmap == NULL || maptemp == NULL) {
             pm_error("Malloc failed for color map of size %d*%d "
                      "in rle_get_setup, reading '%s'",
                      the_hdr->ncmap, (1 << the_hdr->cmaplen),
                      the_hdr->file_name );
             return RLE_NO_SPACE;
         }
-        fread( maptemp, 2, maplen, infile );
-        for ( i = 0; i < maplen; i++ )
-            the_hdr->cmap[i] = vax_gshort( &maptemp[i * 2] );
-        free( maptemp );
+        fread(maptemp, 2, maplen, infile);
+        for (i = 0; i < maplen; ++i)
+            the_hdr->cmap[i] = vax_gshort(&maptemp[i * 2]);
+        free(maptemp);
     }
 
     /* Check for comments */
-    if ( setup.h_flags & H_COMMENT )
-    {
+    if (setup.h_flags & H_COMMENT) {
         short comlen, evenlen;
-        register char * cp;
+        char * cp;
 
-        VAXSHORT( comlen, infile ); /* get comment length */
+        VAXSHORT(comlen, infile); /* get comment length */
         evenlen = (comlen + 1) & ~1;    /* make it even */
-        if ( evenlen )
-        {
+        if (evenlen) {
             MALLOCARRAY(comment_buf, evenlen);
     
-            if ( comment_buf == NULL )
-            {
+            if (comment_buf == NULL) {
                 pm_error("Malloc failed for comment buffer of size %d "
                          "in rle_get_setup, reading '%s'",
                          comlen, the_hdr->file_name );
                 return RLE_NO_SPACE;
             }
-            fread( comment_buf, 1, evenlen, infile );
+            fread(comment_buf, 1, evenlen, infile);
             /* Count the comments */
-            for ( i = 0, cp = comment_buf; cp < comment_buf + comlen; cp++ )
-                if ( *cp == 0 )
-                    i++;
-            i++;            /* extra for NULL pointer at end */
+            for (i = 0, cp = comment_buf; cp < comment_buf + comlen; ++cp)
+                if (*cp == '\0')
+                    ++i;
+            ++i;            /* extra for NULL pointer at end */
             /* Get space to put pointers to comments */
             MALLOCARRAY(the_hdr->comments, i);
-            if ( the_hdr->comments == NULL )
-            {
+            if (the_hdr->comments == NULL) {
                 pm_error("Malloc failed for %d comment pointers "
                          "in rle_get_setup, reading '%s'",
                          i, the_hdr->file_name );
@@ -197,366 +186,331 @@ rle_get_setup(rle_hdr * const the_hdr) {
             }
             /* Get pointers to the comments */
             *the_hdr->comments = comment_buf;
-            for ( i = 1, cp = comment_buf + 1;
-                  cp < comment_buf + comlen;
-                  cp++ )
-                if ( *(cp - 1) == 0 )
+            for (i = 1, cp = comment_buf + 1;
+                 cp < comment_buf + comlen;
+                 ++cp)
+                if (*(cp - 1) == '\0')
                     the_hdr->comments[i++] = cp;
             the_hdr->comments[i] = NULL;
-        }
-        else
+        } else
             the_hdr->comments = NULL;
-    }
-    else
+    } else
         the_hdr->comments = NULL;
 
     /* Initialize state for rle_getrow */
     the_hdr->priv.get.scan_y = the_hdr->ymin;
     the_hdr->priv.get.vert_skip = 0;
     the_hdr->priv.get.is_eof = 0;
-    the_hdr->priv.get.is_seek = ftell( infile ) > 0;
+    the_hdr->priv.get.is_seek = ftell(infile) > 0;
     debug_f = 0;
 
-    if ( !feof( infile ) )
+    if (!feof(infile))
         return RLE_SUCCESS; /* success! */
-    else
-    {
+    else {
         the_hdr->priv.get.is_eof = 1;
         return RLE_EOF;
     }
 }
 
 
-/*****************************************************************
- * TAG( rle_get_setup_ok )
- * 
- * Read the initialization information from an RLE file.
- * Inputs:
- *  the_hdr:    Contains pointer to the input file.
- *  prog_name:  Program name to be printed in the error message.
- *      file_name:  File name to be printed in the error message.
- *                  If NULL, the string "stdin" is generated.
- * Outputs:
- *  the_hdr:    Initialized with information from the
- *          input file.
- *      If reading the header fails, it prints an error message
- *  and exits with the appropriate status code.
- * Algorithm:
- *  rle_get_setup does all the work.
- */
+
 void
-rle_get_setup_ok( the_hdr, prog_name, file_name )
-rle_hdr * the_hdr;
-const char *prog_name;
-const char *file_name;
-{
+rle_get_setup_ok(rle_hdr *    const the_hdr,
+                 const char * const prog_name,
+                 const char * const file_name) {
+/*-----------------------------------------------------------------------------
+  Read the initialization information from an RLE file.
+
+  Inputs:
+   the_hdr:    Contains pointer to the input file.
+   prog_name:  Program name to be printed in the error message.
+       file_name:  File name to be printed in the error message.
+                   If NULL, the string "stdin" is generated.
+
+  Outputs:
+   the_hdr:    Initialized with information from the input file.
+       If reading the header fails, it prints an error message
+       and exits with the appropriate status code.
+  Algorithm:
+   rle_get_setup does all the work.
+---------------------------------------------------------------------------- */
     int code;
 
     /* Backwards compatibility: if is_init is not properly set, 
      * initialize the header.
      */
-    if ( the_hdr->is_init != RLE_INIT_MAGIC )
-    {
-    FILE *f = the_hdr->rle_file;
-    rle_hdr_init( the_hdr );
-    the_hdr->rle_file = f;
-    rle_names( the_hdr, prog_name, file_name, 0 );
+    if (the_hdr->is_init != RLE_INIT_MAGIC) {
+        FILE * const f = the_hdr->rle_file;
+        rle_hdr_init( the_hdr );
+        the_hdr->rle_file = f;
+        rle_names(the_hdr, prog_name, file_name, 0);
     }
 
-    code = rle_get_error( rle_get_setup( the_hdr ),
-              the_hdr->cmd, the_hdr->file_name );
+    code = rle_get_error(rle_get_setup(the_hdr),
+                         the_hdr->cmd, the_hdr->file_name);
     if (code)
-    exit( code );
+        exit(code);
 }
 
 
-/*****************************************************************
- * TAG( rle_debug )
- * 
- * Turn RLE debugging on or off.
- * Inputs:
- *  on_off:     if 0, stop debugging, else start.
- * Outputs:
- *  Sets internal debug flag.
- * Assumptions:
- *  [None]
- * Algorithm:
- *  [None]
- */
+
 void
 rle_debug( on_off )
-int on_off;
+    int on_off;
 {
+/*-----------------------------------------------------------------------------
+  Turn RLE debugging on or off.
+  Inputs:
+   on_off:     if 0, stop debugging, else start.
+  Outputs:
+   Sets internal debug flag.
+  Assumptions:
+   [None]
+  Algorithm:
+   [None]
+---------------------------------------------------------------------------- */
     debug_f = on_off;
 
     /* Set line buffering on stderr.  Character buffering is the default, and
      * it is SLOOWWW for large amounts of output.
      */
-    setvbuf( stderr, NULL, _IOLBF, 0);
-/*
-    setlinebuf( stderr );
-*/
+    setvbuf(stderr, NULL, _IOLBF, 0);
 }
 
 
-/*****************************************************************
- * TAG( rle_getrow )
- * 
- * Get a scanline from the input file.
- * Inputs:
- *  the_hdr:    Header structure containing information about 
- *          the input file.
- * Outputs:
- *  scanline:   an array of pointers to the individual color
- *          scanlines.  Scanline is assumed to have
- *          the_hdr->ncolors pointers to arrays of rle_pixel,
- *          each of which is at least the_hdr->xmax+1 long.
- *  Returns the current scanline number.
- * Assumptions:
- *  rle_get_setup has already been called.
- * Algorithm:
- *  If a vertical skip is being executed, and clear-to-background is
- *  specified (the_hdr->background is true), just set the
- *  scanlines to the background color.  If clear-to-background is
- *  not set, just increment the scanline number and return.
- * 
- *  Otherwise, read input until a vertical skip is encountered,
- *  decoding the instructions into scanline data.
- *
- *  If ymax is reached (or, somehow, passed), continue reading and
- *  discarding input until end of image.
- */
+
 int
-rle_getrow( the_hdr, scanline )
-rle_hdr * the_hdr;
-rle_pixel *scanline[];
-{
-    register rle_pixel * scanc;
-    register int nc;
-    register FILE *infile = the_hdr->rle_file;
-    int scan_x = the_hdr->xmin, /* current X position */
-        max_x = the_hdr->xmax,  /* End of the scanline */
-       channel = 0;         /* current color channel */
+rle_getrow(rle_hdr *    const the_hdr,
+           rle_pixel ** const scanline) {
+/*-----------------------------------------------------------------------------
+  Get a scanline from the input file.
+  Inputs:
+   the_hdr:    Header structure containing information about 
+           the input file.
+  Outputs:
+   scanline:   an array of pointers to the individual color
+           scanlines.  Scanline is assumed to have
+           the_hdr->ncolors pointers to arrays of rle_pixel,
+           each of which is at least the_hdr->xmax+1 long.
+   Returns the current scanline number.
+  Assumptions:
+   rle_get_setup has already been called.
+  Algorithm:
+   If a vertical skip is being executed, and clear-to-background is
+   specified (the_hdr->background is true), just set the
+   scanlines to the background color.  If clear-to-background is
+   not set, just increment the scanline number and return.
+  
+   Otherwise, read input until a vertical skip is encountered,
+   decoding the instructions into scanline data.
+ 
+   If ymax is reached (or, somehow, passed), continue reading and
+   discarding input until end of image.
+---------------------------------------------------------------------------- */
+    FILE * const infile = the_hdr->rle_file;
+
+    rle_pixel * scanc;
+
+    int scan_x; /* current X position */
+    int max_x;  /* End of the scanline */
+    int channel;         /* current color channel */
     int ns;         /* Number to skip */
+    int nc;
     short word, long_data;
     char inst[2];
 
+    scan_x = the_hdr->xmin; /* initial value */
+    max_x = the_hdr->xmax;  /* initial value */
+    channel = 0; /* initial value */
     /* Clear to background if specified */
-    if ( the_hdr->background != 1 )
-    {
-    if ( the_hdr->alpha && RLE_BIT( *the_hdr, -1 ) )
-        memset( (char *)scanline[-1] + the_hdr->xmin, 0,
-           the_hdr->xmax - the_hdr->xmin + 1 );
-    for ( nc = 0; nc < the_hdr->ncolors; nc++ )
-        if ( RLE_BIT( *the_hdr, nc ) ) {
-        /* Unless bg color given explicitly, use 0. */
-        if ( the_hdr->background != 2 || the_hdr->bg_color[nc] == 0 )
-            memset( (char *)scanline[nc] + the_hdr->xmin, 0,
-               the_hdr->xmax - the_hdr->xmin + 1 );
-        else
-            memset((char *)scanline[nc] + the_hdr->xmin,
-                   the_hdr->bg_color[nc],
+    if (the_hdr->background != 1) {
+        if (the_hdr->alpha && RLE_BIT( *the_hdr, -1))
+            memset((char *)scanline[-1] + the_hdr->xmin, 0,
                    the_hdr->xmax - the_hdr->xmin + 1);
+        for (nc = 0; nc < the_hdr->ncolors; ++nc) {
+            if (RLE_BIT( *the_hdr, nc)) {
+                /* Unless bg color given explicitly, use 0. */
+                if (the_hdr->background != 2 || the_hdr->bg_color[nc] == 0)
+                    memset((char *)scanline[nc] + the_hdr->xmin, 0,
+                           the_hdr->xmax - the_hdr->xmin + 1);
+                else
+                    memset((char *)scanline[nc] + the_hdr->xmin,
+                           the_hdr->bg_color[nc],
+                           the_hdr->xmax - the_hdr->xmin + 1);
+            }
         }
     }
 
     /* If skipping, then just return */
-    if ( the_hdr->priv.get.vert_skip > 0 )
-    {
-    the_hdr->priv.get.vert_skip--;
-    the_hdr->priv.get.scan_y++;
-    if ( the_hdr->priv.get.vert_skip > 0 ) {
-        if ( the_hdr->priv.get.scan_y >= the_hdr->ymax )
-        {
-        int y = the_hdr->priv.get.scan_y;
-        while ( rle_getskip( the_hdr ) != 32768 )
-            ;
-        return y;
+    if (the_hdr->priv.get.vert_skip > 0) {
+        --the_hdr->priv.get.vert_skip;
+        ++the_hdr->priv.get.scan_y;
+        if (the_hdr->priv.get.vert_skip > 0) {
+            if (the_hdr->priv.get.scan_y >= the_hdr->ymax) {
+                int const y = the_hdr->priv.get.scan_y;
+                while (rle_getskip(the_hdr) != 32768)
+                    ;
+                return y;
+            } else
+                return the_hdr->priv.get.scan_y;
         }
-        else
-        return the_hdr->priv.get.scan_y;
-    }
     }
 
     /* If EOF has been encountered, return also */
-    if ( the_hdr->priv.get.is_eof )
-    return ++the_hdr->priv.get.scan_y;
+    if (the_hdr->priv.get.is_eof)
+        return ++the_hdr->priv.get.scan_y;
 
     /* Otherwise, read and interpret instructions until a skipLines
-     * instruction is encountered.
-     */
-    if ( RLE_BIT( *the_hdr, channel ) )
-    scanc = scanline[channel] + scan_x;
+       instruction is encountered.
+    */
+    if (RLE_BIT(*the_hdr, channel))
+        scanc = scanline[channel] + scan_x;
     else
-    scanc = NULL;
-    for (;;)
-    {
-    inst[0] = getc( infile );
-    inst[1] = getc( infile );
-    if ( feof(infile) )
-    {
-        the_hdr->priv.get.is_eof = 1;
-        break;      /* <--- one of the exits */
-    }
-
-    switch( OPCODE(inst) )
-    {
-    case RSkipLinesOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( the_hdr->priv.get.vert_skip, infile );
+        scanc = NULL;
+    for (;;) {
+        inst[0] = getc(infile);
+        inst[1] = getc(infile);
+        if (feof(infile)) {
+            the_hdr->priv.get.is_eof = 1;
+            break;      /* <--- one of the exits */
         }
-        else
-        the_hdr->priv.get.vert_skip = DATUM(inst);
-        if (debug_f)
-        fprintf(stderr, "Skip %d Lines (to %d)\n",
-            the_hdr->priv.get.vert_skip,
-            the_hdr->priv.get.scan_y +
-                the_hdr->priv.get.vert_skip );
-
-        break;          /* need to break for() here, too */
-
-    case RSetColorOp:
-        channel = DATUM(inst);  /* select color channel */
-        if ( channel == 255 )
-        channel = -1;
-        scan_x = the_hdr->xmin;
-        if ( RLE_BIT( *the_hdr, channel ) )
-        scanc = scanline[channel]+scan_x;
-        if ( debug_f )
-        fprintf( stderr, "Set color to %d (reset x to %d)\n",
-             channel, scan_x );
-        break;
-
-    case RSkipPixelsOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( long_data, infile );
-        scan_x += long_data;
-        scanc += long_data;
-        if ( debug_f )
-            fprintf( stderr, "Skip %d pixels (to %d)\n",
-                long_data, scan_x );
-        }
-        else
-        {
-        scan_x += DATUM(inst);
-        scanc += DATUM(inst);
-        if ( debug_f )
-            fprintf( stderr, "Skip %d pixels (to %d)\n",
-                DATUM(inst), scan_x );
-        }
-        break;
 
-    case RByteDataOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( nc, infile );
-        }
-        else
-        nc = DATUM(inst);
-        nc++;
-        if ( debug_f ) {
-        if ( RLE_BIT( *the_hdr, channel ) )
-            fprintf( stderr, "Pixel data %d (to %d):", nc, scan_x+nc );
-        else
-            fprintf( stderr, "Pixel data %d (to %d)\n", nc, scan_x+nc);
-        }
-        if ( RLE_BIT( *the_hdr, channel ) )
-        {
-        /* Don't fill past end of scanline! */
-        if ( scan_x + nc > max_x )
-        {
-            ns = scan_x + nc - max_x - 1;
-            nc -= ns;
-        }
-        else
-            ns = 0;
-        fread( (char *)scanc, 1, nc, infile );
-        while ( ns-- > 0 )
-            (void)getc( infile );
-        if ( nc & 1 )
-            (void)getc( infile );   /* throw away odd byte */
-        }
-        else
-        if ( the_hdr->priv.get.is_seek )
-            fseek( infile, ((nc + 1) / 2) * 2, 1 );
-        else
-        {
-            register int ii;
-            for ( ii = ((nc + 1) / 2) * 2; ii > 0; ii-- )
-            (void) getc( infile );  /* discard it */
-        }
+        switch(OPCODE(inst)) {
+        case RSkipLinesOp:
+            if (LONGP(inst)) {
+                VAXSHORT(the_hdr->priv.get.vert_skip, infile);
+            } else
+                the_hdr->priv.get.vert_skip = DATUM(inst);
+            if (debug_f)
+                pm_message("Skip %d Lines (to %d)",
+                           the_hdr->priv.get.vert_skip,
+                           the_hdr->priv.get.scan_y +
+                           the_hdr->priv.get.vert_skip);
+
+            break;          /* need to break for() here, too */
+
+        case RSetColorOp:
+            channel = DATUM(inst);  /* select color channel */
+            if (channel == 255)
+                channel = -1;
+            scan_x = the_hdr->xmin;
+            if (RLE_BIT(*the_hdr, channel))
+                scanc = scanline[channel]+scan_x;
+            if (debug_f)
+                pm_message("Set color to %d (reset x to %d)",
+                           channel, scan_x );
+            break;
+
+        case RSkipPixelsOp:
+            if (LONGP(inst)) {
+                VAXSHORT(long_data, infile);
+                scan_x += long_data;
+                scanc += long_data;
+                if (debug_f)
+                    pm_message("Skip %d pixels (to %d)", long_data, scan_x);
+            } else {
+                scan_x += DATUM(inst);
+                scanc += DATUM(inst);
+                if (debug_f)
+                    pm_message("Skip %d pixels (to %d)", DATUM(inst), scan_x);
+            }
+            break;
+
+        case RByteDataOp:
+            if (LONGP(inst)) {
+                VAXSHORT(nc, infile);
+            } else
+                nc = DATUM(inst);
+            ++nc;
+            if (debug_f) {
+                if (RLE_BIT(*the_hdr, channel))
+                    pm_message("Pixel data %d (to %d):", nc, scan_x + nc);
+                else
+                    pm_message("Pixel data %d (to %d)", nc, scan_x + nc);
+            }
+            if (RLE_BIT(*the_hdr, channel)) {
+                /* Don't fill past end of scanline! */
+                if (scan_x + nc > max_x) {
+                    ns = scan_x + nc - max_x - 1;
+                    nc -= ns;
+                } else
+                    ns = 0;
+                fread((char *)scanc, 1, nc, infile);
+                while (ns-- > 0)
+                    getc(infile);
+                if (nc & 0x1)
+                    getc(infile);   /* throw away odd byte */
+            } else {
+                if (the_hdr->priv.get.is_seek)
+                    fseek(infile, ((nc + 1) / 2) * 2, 1);
+                else {
+                    int ii;
+                    for (ii = ((nc + 1) / 2) * 2; ii > 0; --ii)
+                        getc(infile);  /* discard it */
+                }
+            }
+            scanc += nc;
+            scan_x += nc;
+            if (debug_f && RLE_BIT(*the_hdr, channel)) {
+                rle_pixel * cp;
+                for (cp = scanc - nc; nc > 0; --nc)
+                    fprintf(stderr, "%02x", *cp++);
+                putc('\n', stderr);
+            }
+            break;
+
+        case RRunDataOp:
+            if (LONGP(inst)) {
+                VAXSHORT(nc, infile);
+            } else
+                nc = DATUM(inst);
+            ++nc;
+            scan_x += nc;
+
+            VAXSHORT(word, infile);
+            if (debug_f)
+                pm_message("Run length %d (to %d), data %02x",
+                           nc, scan_x, word);
+            if (RLE_BIT(*the_hdr, channel)) {
+                if (scan_x > max_x) {
+                    ns = scan_x - max_x - 1;
+                    nc -= ns;
+                } else
+                    ns = 0;
+                if (nc >= 10) {    /* break point for 785, anyway */
+                    memset((char *)scanc, word, nc);
+                    scanc += nc;
+                } else {
+                    for (nc--; nc >= 0; --nc, ++scanc)
+                        *scanc = word;
+                }
+            }
+            break;
 
-        scanc += nc;
-        scan_x += nc;
-        if ( debug_f && RLE_BIT( *the_hdr, channel ) )
-        {
-        rle_pixel * cp = scanc - nc;
-        for ( ; nc > 0; nc-- )
-            fprintf( stderr, "%02x", *cp++ );
-        putc( '\n', stderr );
-        }
-        break;
+        case REOFOp:
+            the_hdr->priv.get.is_eof = 1;
+            if (debug_f)
+                pm_message("End of Image");
+            break;
 
-    case RRunDataOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( nc, infile );
+        default:
+            pm_error("rle_getrow: Unrecognized opcode: %d, reading %s",
+                     inst[0], the_hdr->file_name);
         }
-        else
-        nc = DATUM(inst);
-        nc++;
-        scan_x += nc;
-
-        VAXSHORT( word, infile );
-        if ( debug_f )
-        fprintf( stderr, "Run length %d (to %d), data %02x\n",
-                nc, scan_x, word );
-        if ( RLE_BIT( *the_hdr, channel ) )
-        {
-        if ( scan_x > max_x )
-        {
-            ns = scan_x - max_x - 1;
-            nc -= ns;
-        } 
-        else
-            ns = 0;
-        if ( nc >= 10 )     /* break point for 785, anyway */
-        {
-            memset((char *)scanc, word, nc);
-            scanc += nc;
-        }
-        else
-            for ( nc--; nc >= 0; nc--, scanc++ )
-            *scanc = word;
-        }
-        break;
-
-    case REOFOp:
-        the_hdr->priv.get.is_eof = 1;
-        if ( debug_f )
-        fprintf( stderr, "End of Image\n" );
-        break;
-
-    default:
-        fprintf( stderr,
-             "%s: rle_getrow: Unrecognized opcode: %d, reading %s\n",
-             the_hdr->cmd, inst[0], the_hdr->file_name );
-        exit(1);
-    }
-    if ( OPCODE(inst) == RSkipLinesOp || OPCODE(inst) == REOFOp )
-        break;          /* <--- the other loop exit */
+        if (OPCODE(inst) == RSkipLinesOp || OPCODE(inst) == REOFOp)
+            break;          /* <--- the other loop exit */
     }
 
     /* If at end, skip the rest of a malformed image. */
-    if ( the_hdr->priv.get.scan_y >= the_hdr->ymax )
-    {
-    int y = the_hdr->priv.get.scan_y;
-    while ( rle_getskip( the_hdr ) != 32768 )
-        ;
-    return y;
+    if (the_hdr->priv.get.scan_y >= the_hdr->ymax) {
+        int const y = the_hdr->priv.get.scan_y;
+        while (rle_getskip(the_hdr) != 32768 )
+            ;
+        return y;
     }
 
     return the_hdr->priv.get.scan_y;
 }
+
+
+
diff --git a/version.mk b/version.mk
index 9089dbdd..8ef2cb63 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 71
-NETPBM_POINT_RELEASE = 3
+NETPBM_MINOR_RELEASE = 72
+NETPBM_POINT_RELEASE = 0