about summary refs log tree commit diff
path: root/converter/other
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other')
-rw-r--r--converter/other/Makefile58
-rwxr-xr-xconverter/other/anytopnm46
-rw-r--r--converter/other/bmepsoe.c217
-rw-r--r--converter/other/bmptopnm.c114
-rw-r--r--converter/other/cameratopam/Makefile7
-rw-r--r--converter/other/cameratopam/cameratopam.c22
-rw-r--r--converter/other/dithers.h91
-rw-r--r--converter/other/fiasco/Makefile6
-rw-r--r--converter/other/fiasco/codec/Makefile6
-rw-r--r--converter/other/fiasco/input/Makefile8
-rw-r--r--converter/other/fiasco/lib/Makefile6
-rw-r--r--converter/other/fiasco/lib/misc.c32
-rw-r--r--converter/other/fiasco/output/Makefile11
-rw-r--r--converter/other/fiasco/params.c3
-rw-r--r--converter/other/fitstopnm.c889
-rw-r--r--converter/other/giftopnm.c731
-rw-r--r--converter/other/hdifftopam.c5
-rw-r--r--converter/other/infotopam.c1
-rw-r--r--converter/other/jbig/Makefile8
-rw-r--r--converter/other/jpeg2000/Makefile8
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c1
-rw-r--r--converter/other/jpeg2000/libjasper/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/base/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_stream.c71
-rw-r--r--converter/other/jpeg2000/libjasper/common.mk (renamed from converter/other/jpeg2000/libjasper/Makefile.common)7
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig228
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_math.c128
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c34
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c1
-rw-r--r--converter/other/jpegdatasource.c36
-rw-r--r--converter/other/jpegdatasource.h3
-rw-r--r--converter/other/jpegtopnm.c153
-rw-r--r--converter/other/pamtodjvurle.c3
-rw-r--r--converter/other/pamtofits.c27
-rw-r--r--converter/other/pamtogif.c1977
-rw-r--r--converter/other/pamtohdiff.c1
-rw-r--r--converter/other/pamtohtmltbl.c106
-rw-r--r--converter/other/pamtompfont.c182
-rw-r--r--converter/other/pamtooctaveimg.c241
-rw-r--r--converter/other/pamtopam.c57
-rw-r--r--converter/other/pamtopfm.c1
-rw-r--r--converter/other/pamtopnm.c60
-rw-r--r--converter/other/pamtosvg/Makefile4
-rw-r--r--converter/other/pamtosvg/curve.c197
-rw-r--r--converter/other/pamtosvg/curve.h46
-rw-r--r--converter/other/pamtosvg/fit.c1870
-rw-r--r--converter/other/pamtosvg/pamtosvg.c4
-rw-r--r--converter/other/pamtosvg/pamtosvg.test6
-rw-r--r--converter/other/pamtosvg/pxl-outline.c45
-rw-r--r--converter/other/pamtosvg/spline.c10
-rw-r--r--converter/other/pamtosvg/spline.h1
-rw-r--r--converter/other/pamtosvg/vector.c282
-rw-r--r--converter/other/pamtosvg/vector.h70
-rw-r--r--converter/other/pamtotga.c3
-rw-r--r--converter/other/pamtotiff.c13
-rw-r--r--converter/other/pamtouil.c64
-rw-r--r--converter/other/pamtoxvmini.c3
-rw-r--r--converter/other/pfmtopam.c1
-rw-r--r--converter/other/pgmtopbm.c50
-rw-r--r--converter/other/pgmtoppm.c345
-rw-r--r--converter/other/pngtopam.c1281
-rw-r--r--converter/other/pngtopnm.c965
-rw-r--r--converter/other/pnmtoddif.c268
-rw-r--r--converter/other/pnmtojpeg.c726
-rw-r--r--converter/other/pnmtopalm/Makefile9
-rw-r--r--converter/other/pnmtopalm/gen_palm_colormap.c48
-rw-r--r--converter/other/pnmtopalm/palmcolor8.map458
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c1
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c1
-rw-r--r--converter/other/pnmtopclxl.c5
-rw-r--r--converter/other/pnmtopng.c132
-rw-r--r--converter/other/pnmtops.c76
-rw-r--r--converter/other/pnmtorast.c139
-rw-r--r--converter/other/pnmtorle.c198
-rw-r--r--converter/other/pnmtotiffcmyk.c6
-rw-r--r--converter/other/pnmtoxwd.c4
-rw-r--r--converter/other/pstopnm.c150
-rw-r--r--converter/other/rast.h30
-rw-r--r--converter/other/rlatopam.c1
-rw-r--r--converter/other/rletopnm.c5
-rw-r--r--converter/other/svgtopam.c34
-rw-r--r--converter/other/tiff.c6
-rw-r--r--converter/other/tifftopnm.c891
-rw-r--r--converter/other/x11wd.h8
-rw-r--r--converter/other/xwdtopnm.c537
87 files changed, 10267 insertions, 4328 deletions
diff --git a/converter/other/Makefile b/converter/other/Makefile
index a83eeb21..df084ceb 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 ifeq ($(shell xml2-config --version),)
   XML2_LIBS=NONE
@@ -20,41 +20,40 @@ ifneq ($(BUILD_FIASCO), N)
   SUBDIRS += fiasco
 endif
 
-INCLUDES = -I$(SRCDIR)/util 
 ifneq ($(TIFFLIB),NONE)
   ifneq ($(TIFFHDR_DIR)x,x)
-    INCLUDES += -I$(TIFFHDR_DIR)
+    EXTERN_INCLUDES += -I$(TIFFHDR_DIR)
   endif
 endif
 
-ifeq ($(shell libpng-config --version),)
+ifeq ($(shell libpng$(PNGVER)-config --version),)
   ifneq ($(PNGLIB),NONE)
     HAVE_PNGLIB = Y
     ifneq ($(PNGHDR_DIR)x,x)
-      INCLUDES += -I$(PNGHDR_DIR)
+      EXTERN_INCLUDES += -I$(PNGHDR_DIR)
     endif
     ifneq ($(ZHDR_DIR)x,x)
-      INCLUDES += -I$(ZHDR_DIR)
+      EXTERN_INCLUDES += -I$(ZHDR_DIR)
     endif
   endif
 else
   HAVE_PNGLIB = Y
-  INCLUDES += $(shell libpng-config --cflags)
+  EXTERN_INCLUDES += $(shell libpng$(PNGVER)-config --cflags)
 endif
 
 ifneq ($(JPEGLIB),NONE)
   ifneq ($(JPEGHDR_DIR)x,x)
-    INCLUDES += -I$(JPEGHDR_DIR)
+    EXTERN_INCLUDES += -I$(JPEGHDR_DIR)
   endif
 endif
 ifneq ($(URTLIB),NONE)
   ifneq ($(URTHDR_DIR)x,x)
-    INCLUDES += -I$(URTHDR_DIR)
+    EXTERN_INCLUDES += -I$(URTHDR_DIR)
   endif
 endif
 ifneq ($(XML2_LIBS),NONE)
   ifneq ($(XML2_CFLAGS),NONE)
-    INCLUDES += $(XML2_CFLAGS)
+    EXTERN_INCLUDES += $(XML2_CFLAGS)
   endif
 endif
 
@@ -80,19 +79,24 @@ endif
 
 PORTBINARIES =  bmptopnm fitstopnm \
 		gemtopnm giftopnm hdifftopam infotopam \
-		pamtodjvurle pamtofits \
-		pamtohdiff pamtohtmltbl pamtopfm pamtopnm pamtouil \
+		pamtodjvurle pamtofits pamtogif \
+		pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \
+		pamtopam pamtopfm pamtopnm pamtouil \
 		pamtoxvmini \
 		pbmtopgm pfmtopam \
 	        pgmtopbm pgmtoppm ppmtopgm pnmtoddif \
 		pnmtopclxl \
-		pnmtosgi pnmtosir pamtotga pnmtoxwd pstopnm \
+		pnmtosgi pnmtosir pamtotga pnmtoxwd \
 		rlatopam sgitopnm sirtopnm xwdtopnm zeisstopnm
 
+ifneq ($(DONT_HAVE_PROCESS_MGMT),Y)
+  PORTBINARIES += pstopnm
+endif
+
 BINARIES = $(PORTBINARIES) pnmtorast rasttopnm
 
 ifeq ($(HAVE_PNGLIB),Y)
-  BINARIES += pnmtopng pngtopnm pamrgbatopng
+  BINARIES += pnmtopng pngtopnm pngtopam pamrgbatopng
 endif
 ifneq ($(JPEGLIB),NONE)
   BINARIES += jpegtopnm pnmtojpeg
@@ -133,7 +137,7 @@ SCRIPTS = anytopnm pnmtoplainpnm
 .PHONY: all
 all:	$(BINARIES) $(SUBDIRS:%=%/all)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ifeq ($(NEED_RUNTIME_PATH),Y)
   LIBOPTR = -runtime
@@ -148,13 +152,13 @@ tifftopnm pamtotiff pnmtotiffcmyk: %: %.o tiff.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $@.o tiff.o \
 	  $(LIBOPTS_TIFF) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
-ifeq ($(shell libpng-config --version),)
+ifeq ($(shell libpng$(PNGVER)-config --version),)
   PNGLIB_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
 else
-  PNGLIB_LIBOPTS = $(shell libpng-config --ldflags)
+  PNGLIB_LIBOPTS = $(shell libpng$(PNGVER)-config --ldflags)
 endif
 
-pngtopnm: %: %.o $(NETPBMLIB) $(LIBOPT)
+pngtopnm pngtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $@.o \
 	  $(shell $(LIBOPT) $(NETPBMLIB)) \
 	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
@@ -218,27 +222,29 @@ install.bin.local: $(PKGDIR)/bin
 	$(SYMLINK) pnmtoplainpnm pnmnoraw
 # backward compatibility: program used to be gemtopbm
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) gemtopnm$(EXE) gemtopbm
+	$(SYMLINK) gemtopnm$(EXE) gemtopbm$(EXE)
 # In October 2001, pnmtojpeg replaced ppmtojpeg
+ifneq ($(JPEGLIB),NONE)
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pnmtojpeg$(EXE) ppmtojpeg
+	$(SYMLINK) pnmtojpeg$(EXE) ppmtojpeg$(EXE)
+endif
 # In March 2002, bmptopnm replaced bmptoppm
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) bmptopnm$(EXE) bmptoppm
+	$(SYMLINK) bmptopnm$(EXE) bmptoppm$(EXE)
 # In May 2002, pamtouil replaced ppmtouil
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtouil$(EXE) ppmtouil
+	$(SYMLINK) pamtouil$(EXE) ppmtouil$(EXE)
 # In July 2002, pamtotga replaced ppmtotga
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pamtotga$(EXE) ppmtotga$(EXE)
 # In March 2005, we realized that pamtopnm obviates pnmtopnm
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtopnm$(EXE) pnmtopnm
+	$(SYMLINK) pamtopnm$(EXE) pnmtopnm$(EXE)
 # In October 2005, pamtofits replaced pnmtofits
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtofits$(EXE) pnmtofits
+	$(SYMLINK) pamtofits$(EXE) pnmtofits$(EXE)
 ifneq ($(TIFF_PREREQ_MISSING),Y)
 # In October 2005, pamtotiff replaced pnmtotiff
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtotiff$(EXE) pnmtotiff
-endif
\ No newline at end of file
+	$(SYMLINK) pamtotiff$(EXE) pnmtotiff$(EXE)
+endif
diff --git a/converter/other/anytopnm b/converter/other/anytopnm
index b2873772..06f48b4f 100755
--- a/converter/other/anytopnm
+++ b/converter/other/anytopnm
@@ -47,18 +47,39 @@ putInputIntoTempfile() {
 
 
 setMimeType() {
+#------------------------------------------------------------------------------
+# Set variable 'mimeType' to the mime type string (e.g. "application/binary")
+#
+# If we can't tell, set it to "unknown".
+#------------------------------------------------------------------------------
     # $1 is the file name
 
-    # Christos Zoulas's current 'file' (see Freshmeat) has the --mime option.
+    # Old versions of 'file' cannot tell us the mime type; they lack any option
+    # to do so.
+    #
+    # Newer ones have a --mime option for that.
+    #
+    # Still newer ones (ca 2008) have a --mime option, but it does something
+    # different - it prints the mime type plus the mime encoding.  And they
+    # have --mime-type to print just the mime type.
 
-    file --mime /dev/null >/dev/null 2>/dev/null
+    file --mime-type /dev/null >/dev/null 2>/dev/null
     if [ $? -eq 0 ]; then
-        # Now that we know the --mime option exists, use it.
-        mimeType=`file --mime "$1" | cut -d: -f2- | cut -c2-`
+        # Now that we know the --mime-type option exists, use it.
+        mimeType=`file --mime-type "$1" | cut -d: -f2- | cut -c2-`
     else
-        # file --mime failed; we don't know why, but we assume it's because it
-        # is a traditional 'file' program that doesn't have a --mime option.
-    mimeType="unknown"
+        # file --mime-type failed; we don't know why, but we assume it's
+        # because it is an older 'file' program that doesn't have a --mime-type
+        # option.
+        file --mime /dev/null >/dev/null 2>/dev/null
+        if [ $? -eq 0 ]; then
+            # Now that we know the --mime option exists, use it.
+            mimeType=`file --mime "$1" | cut -d: -f2- | cut -c2-`
+        else
+            # file --mime failed; we don't know why, but we assume it's because
+            # it is an older 'file' program that doesn't have a --mime option.
+            mimeType="unknown"
+        fi
     fi
 }
 
@@ -109,7 +130,7 @@ computeTypeFromTypeDescription () {
 
     case "$1" in
     
-        *PBM* | *PGM* | *PPM* )
+        *PBM* | *PGM* | *PPM* | *Netpbm*PAM*)
             filetype=pnm
             ;;
     
@@ -149,6 +170,10 @@ computeTypeFromTypeDescription () {
             filetype=gzip
             ;;
     
+        *XWD*X*Window*Dump* ) 
+            filetype=xwd
+            ;;
+
         *compress* )
             filetype=compress
             ;;
@@ -507,9 +532,8 @@ else
 fi
 
 tempdir="${TMPDIR-/tmp}/anytopnm.$$"
-mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
-chmod 700 $tempdir
-
+mkdir -m 0700 $tempdir || \
+  { echo "Could not create temporary file. Exiting."; exit 1;}
 trap 'rm -rf $tempdir' 0
 
 # Take out all spaces
diff --git a/converter/other/bmepsoe.c b/converter/other/bmepsoe.c
index cdb52779..02bf39aa 100644
--- a/converter/other/bmepsoe.c
+++ b/converter/other/bmepsoe.c
@@ -209,76 +209,85 @@ static void after_flate_add(Output_Encoder *o, int b)
   
 }
 
-static void do_flate_flush(Output_Encoder *o, int final)
-{
-  Bytef *iptr, *optr, *xptr;
-  uLong  is, os, xs;
-  int err, must_continue;
-  
-  iptr = o->fl_i_buffer; optr = o->fl_o_buffer;
-  is = o->fl_i_size; os = o->fl_o_size;
-  
-  if(iptr && optr && is && os) {
-    is = o->fl_i_used;
-    if(is) {
-      (o->flate_stream).next_in = iptr;
-      (o->flate_stream).avail_in = is;
-      if(final) { 
-    must_continue = 1;
-    while(must_continue) {
-      (o->flate_stream).next_out = optr;
-      (o->flate_stream).avail_out = os;
-      must_continue = 0;
-      err = deflate(&(o->flate_stream), Z_FINISH);
-      switch(err) {
-        case Z_STREAM_END: { 
-              xptr = optr;
+
+
+static void
+do_flate_flush(Output_Encoder * const oP,
+               int              const final) {
+
+    Bytef *iptr, *optr, *xptr;
+    unsigned long is;
+    unsigned long os;
+    unsigned long xs;
+    int err;
+    int mustContinue;
+  
+    iptr = oP->fl_i_buffer; optr = oP->fl_o_buffer;
+    is = oP->fl_i_size; os = oP->fl_o_size;
+  
+    if (iptr && optr && is && os) {
+        is = oP->fl_i_used;
+        if (is || final) {
+            oP->flate_stream.next_in = iptr;
+            oP->flate_stream.avail_in = is;
+            if (final) { 
+                mustContinue = 1;
+                while (mustContinue) {
+                    oP->flate_stream.next_out = optr;
+                    oP->flate_stream.avail_out = os;
+                    mustContinue = 0;
+                    err = deflate(&oP->flate_stream, Z_FINISH);
+                    switch (err) {
+                    case Z_STREAM_END: { 
+                        xptr = optr;
           
-          xs = os - ((o->flate_stream).avail_out);
-          while(xs--) {
-        after_flate_add(o, (*(xptr++)));
-          }
-        } break;
-        case Z_OK : {
-          must_continue = 1;
-              xptr = optr;
+                        xs = os - (oP->flate_stream.avail_out);
+                        while (xs--) {
+                            after_flate_add(oP, *(xptr++));
+                        }
+                    } break;
+                    case Z_OK : {
+                        mustContinue = 1;
+                        xptr = optr;
           
-          xs = os - ((o->flate_stream).avail_out);
-          while(xs--) {
-        after_flate_add(o, (*(xptr++)));
-          }
-        } break;
-        default : { 
-        } break;
-      }
-    }
-      } else { 
-    must_continue = 1;
-    while(must_continue) {
-      must_continue = 0;
-      (o->flate_stream).avail_out = os; (o->flate_stream).next_out = optr;
-      err = deflate(&(o->flate_stream), 0);
-      switch(err) {
-        case Z_OK: {
-          if((o->flate_stream).avail_in) {
-        must_continue = 1;
-          }
+                        xs = os - (oP->flate_stream.avail_out);
+                        while (xs--) {
+                            after_flate_add(oP, *(xptr++));
+                        }
+                    } break;
+                    default : { 
+                    } break;
+                    }
+                }
+            } else { 
+                mustContinue = 1;
+                while (mustContinue) {
+                    mustContinue = 0;
+                    oP->flate_stream.avail_out = os;
+                    oP->flate_stream.next_out = optr;
+                    err = deflate(&oP->flate_stream, 0);
+                    switch (err) {
+                    case Z_OK: {
+                        if (oP->flate_stream.avail_in) {
+                            mustContinue = 1;
+                        }
           
-          xptr = optr; xs = os - ((o->flate_stream).avail_out);
-          while(xs--) {
-        after_flate_add(o, (*(xptr++)));
-          }
-        } break;
-        default : { 
-        } break;
-      }
-    }
-      }
+                        xptr = optr; xs = os - (oP->flate_stream.avail_out);
+                        while (xs--) {
+                            after_flate_add(oP, *(xptr++));
+                        }
+                    } break;
+                    default : { 
+                    } break;
+                    }
+                }
+            }
+        }
     }
-  }
-  
 }
 
+
+
 static void flate_add(Output_Encoder *o, int b)
 {
   Byte bt;
@@ -457,17 +466,20 @@ static void rl_flush(Output_Encoder *o)
   
 }
 
-static void internal_byte_add(Output_Encoder *o, int b)
-{
-  
-  if((o->mode) & OE_RL) {
-    rl_add(o,b);
-  } else {
-    after_rl_add(o,b);
-  }
+
+
+static void
+internal_byte_add(Output_Encoder * const oP,
+                  int              const b) {
   
+    if (oP->mode & OE_RL)
+        rl_add(oP, b);
+    else
+        after_rl_add(oP, b);
 }
 
+
+
 static void internal_byte_flush(Output_Encoder *o)
 {
   
@@ -479,19 +491,23 @@ static void internal_byte_flush(Output_Encoder *o)
   
 }
 
-void oe_bit_add(Output_Encoder *o, int b)
-{
-  
-  o->bit_value = 2 * o->bit_value + (b ? 1 : 0);
-  o->bit_consumed = o->bit_consumed + 1;
-  if(o->bit_consumed >= 8) {
-    o->bit_consumed = 0;
-    internal_byte_add(o, (o->bit_value));
-    o->bit_value = 0;
-  }
+
+
+void
+oe_bit_add(Output_Encoder * const oP,
+           int              const b) {
   
+    oP->bit_value = 2 * oP->bit_value + (b ? 1 : 0);
+    oP->bit_consumed = oP->bit_consumed + 1;
+    if (oP->bit_consumed >= 8) {
+        oP->bit_consumed = 0;
+        internal_byte_add(oP, oP->bit_value);
+        oP->bit_value = 0;
+    }
 }
 
+
+
 void oe_bit_flush(Output_Encoder *o)
 {
   
@@ -511,26 +527,31 @@ void oe_bit_flush(Output_Encoder *o)
   
 }
 
-void oe_byte_add(Output_Encoder *o, int b)
-{
+
+
+void
+oe_byte_add(Output_Encoder * const oP,
+            int              const b) {
   
-  if(o->bit_consumed) {
-    int testval,i;
-    testval = 128;
-    for(i = 0; i < 8; i++) {
-      if(b & testval) {
-    oe_bit_add(o,1);
-      } else {
-    oe_bit_add(o,0);
-      }
-      testval = testval / 2;
+    if (oP->bit_consumed) {
+        int testval;
+        int i;
+        testval = 128;
+        for (i = 0; i < 8; ++i) {
+            if (b & testval) {
+                oe_bit_add(oP, 1);
+            } else {
+                oe_bit_add(oP, 0);
+            }
+            testval = testval / 2;
+        }
+    } else {
+        internal_byte_add(oP, b);
     }
-  } else {
-    internal_byte_add(o,b);
-  }
-  
 }
 
+
+
 void oe_byte_flush(Output_Encoder *o)
 {
   
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
index e1bd1403..9a405d70 100644
--- a/converter/other/bmptopnm.c
+++ b/converter/other/bmptopnm.c
@@ -17,11 +17,17 @@
  in supporting documentation.  This software is provided "as is"
  without express or implied warranty.
 
+ Note: From mid-2003 to mid-2007, this program would crash on any 16
+ bit BMP without transparency and no one reported it.  Before that, it
+ refused to even try to read a 16 bit BMP.  I conclude that essentially
+ nobody is using 16 bit BMP.
+
 *****************************************************************************/
 #include <string.h>
 #include <limits.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -42,6 +48,10 @@ struct bitPosition {
 
        Example: if 16 bits are laid out as XRRRRRGGGGGBBBBB then the shift
        count for the R component is 10 and the mask is 0000000000011111.
+
+       A 'mask' of zero denotes absence of any bits; e.g. in the example
+       above, the mask for the transparency component is zero because there
+       is no transparency component .  'shift' is arbitrary in that case.
     */
     unsigned int shift;
         /* How many bits right you have to shift the value to get the subject
@@ -306,6 +316,36 @@ readOs2InfoHeader(FILE *                 const ifP,
 
 
 static void
+validateCompression(unsigned long const compression,
+                    enum rowOrder const rowOrder,
+                    unsigned int  const cBitCount) {
+    
+    if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
+        compression != COMP_RLE4 && compression != COMP_RLE8 ) 
+        pm_error("Input has unknown encoding.  "
+                 "Compression type code = %ld.  The only ones we know "
+                 "are RGB (%u), BITFIELDS (%u), "
+                 "RLE4 (%u), and RLE8 (%u)",
+                 compression, COMP_RGB, COMP_BITFIELDS,
+                 COMP_RLE4, COMP_RLE8);
+                     
+    if ((compression == COMP_RLE4 || compression == COMP_RLE8) &&
+        rowOrder == TOPDOWN )                        
+        pm_error("Invalid BMP header.  Claims image is top-down and also "
+                 "compressed, which is an impossible combination.");
+
+    if ( (compression == COMP_RLE4 && cBitCount !=4) ||
+         (compression == COMP_RLE8 && cBitCount !=8) ) 
+        pm_error("Invalid BMP header.  " 
+                 "Compression type (%s) disagrees with "
+                 "number of bits per pixel (%u).",
+                 compression == COMP_RLE4 ? "RLE4" : "RLE8",
+                 cBitCount);
+}
+
+
+
+static void
 readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
                                  struct bmpInfoHeader * const headerP) {
 /*----------------------------------------------------------------------------
@@ -340,22 +380,9 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
         unsigned long int const compression = GetLong(ifP);
 
         headerP->bitFields = (compression == COMP_BITFIELDS);
-    
-        if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
-            compression != COMP_RLE4 && compression != COMP_RLE8 ) 
-            pm_error("Input is compressed.  Unsupported encoding method.\n"
-                     "Compression type code = %ld", compression);
-                     
-        if ( (compression == COMP_RLE4 || compression == COMP_RLE8) &&
-              headerP->rowOrder == TOPDOWN )                        
-            pm_error("Invalid BMP header.  Top-down images cannot be compressed.");
 
-        if ( (compression == COMP_RLE4 && headerP->cBitCount !=4) ||
-             (compression == COMP_RLE8 && headerP->cBitCount !=8) )                        
-            pm_error("Invalid BMP header.\n" 
-                     "Compression type (%s) disagrees with number of bits per pixel (%u).",
-                      compression == COMP_RLE4 ? "RLE4" : "RLE8",
-                      headerP->cBitCount);
+        validateCompression(compression, headerP->rowOrder,
+                            headerP->cBitCount);
 
         headerP->compression = compression;             
     }
@@ -399,7 +426,7 @@ lsbZeroCount(unsigned int const mask)
    Use GCC built-in when available.
 -----------------------------------------------------------------------------*/
 
-#if ( defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) )
+#if HAVE_GCC_BITCOUNT 
 {
       return ( mask==0 ? sizeof(mask)*8 : __builtin_ctz(mask) );
 }
@@ -463,6 +490,10 @@ defaultPixelformat(unsigned int const bitCount) {
 
     switch (bitCount) {
     case 16:
+        /* This layout is sometimes called "RGB555".  A document from
+           Microsoft says this is the default (when the "compression"
+           field of the header says COMP_BITFIELDS).
+        */
         retval.conventionalBgr = FALSE;
         retval.red.shift = 10;
         retval.grn.shift = 5;
@@ -500,6 +531,11 @@ readV4InfoHeaderExtension(FILE *                 const ifP,
                           struct bmpInfoHeader * const headerP) {
 
     if (headerP->bitFields) {
+        /* A document from Microsoft says on Windows 95 there is no
+           transparency plane and (red, green, blue) must be either
+           (5,5,5) or (5,6,5) for 16 bit and (8,8,8) for 32 bit.
+           It calls these RGB555, RGB565, RGB888.
+        */
         headerP->pixelformat.red = bitPositionFromMask(GetLong(ifP));
         headerP->pixelformat.grn = bitPositionFromMask(GetLong(ifP));
         headerP->pixelformat.blu = bitPositionFromMask(GetLong(ifP));
@@ -655,16 +691,16 @@ extractBitFields(unsigned int       const rasterval,
         (rasterval >> pixelformat.blu.shift) & pixelformat.blu.mask;
     unsigned int const abits = 
         (rasterval >> pixelformat.trn.shift) & pixelformat.trn.mask;
-    
-    *rP = pixelformat.red.mask ?
-              (unsigned int) rbits * maxval / pixelformat.red.mask : 0;
-    *gP = pixelformat.grn.mask ?
-              (unsigned int) gbits * maxval / pixelformat.grn.mask : 0;
-    *bP = pixelformat.blu.mask ?
-              (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
-    *aP = pixelformat.trn.mask ?
-              (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
-}
+
+    *rP = pixelformat.red.mask > 0 ?
+        (unsigned int) rbits * maxval / pixelformat.red.mask : 0;
+    *gP = pixelformat.grn.mask > 0 ?
+        (unsigned int) gbits * maxval / pixelformat.grn.mask : 0;
+    *bP = pixelformat.blu.mask > 0 ?
+        (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
+    *aP = pixelformat.trn.mask > 0 ?
+        (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
+}        
 
 
 
@@ -1220,7 +1256,7 @@ readColorMap(FILE *               const ifP,
                    bytesRead,
                    BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
                                   BMPheader.cmapsize));
-}
+    }
 }
 
 
@@ -1259,6 +1295,25 @@ warnIfBadFileSize(struct bmpInfoHeader const BMPheader,
 
 
 
+static bool
+isValidBmpBpp(unsigned int const cBitCount) {
+
+    switch (cBitCount) {
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+    case 16:
+    case 24:
+    case 32:
+        return true;
+    default:
+        return false;
+    }
+}
+
+
+
 static void
 readBmp(FILE *               const ifP, 
         unsigned char ***    const BMPrasterP, 
@@ -1316,6 +1371,11 @@ readBmp(FILE *               const ifP,
     if (fgetc(ifP) != EOF)
         pm_message("warning: some image data remains unread.");
     
+    if (!isValidBmpBpp(BMPheader.cBitCount))
+        pm_error("Invalid BMP image: 'cBitCount' field of header "
+                 "(number of bits for each pixel in raster) is %u",
+                 BMPheader.cBitCount);
+
     *colsP        = BMPheader.cols;
     *rowsP        = BMPheader.rows;
     *cBitCountP   = BMPheader.cBitCount;
diff --git a/converter/other/cameratopam/Makefile b/converter/other/cameratopam/Makefile
index b6778c67..20a95aa2 100644
--- a/converter/other/cameratopam/Makefile
+++ b/converter/other/cameratopam/Makefile
@@ -5,14 +5,15 @@ endif
 SUBDIR = converter/other/cameratopam
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
+EXTERN_INCLUDES =
 ifneq ($(JPEGLIB),NONE)
   ifneq ($(JPEGHDR_DIR)x,x)
-    INCLUDES += -I$(JPEGHDR_DIR)
+    EXTERN_INCLUDES += -I$(JPEGHDR_DIR)
     CFLAGS += -DHAVE_JPEG
   endif
 endif
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 
 .PHONY: all
@@ -27,7 +28,7 @@ BINARIES = cameratopam
 MERGEBINARIES = 
 SCRIPTS = 
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 cameratopam: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ \
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
index 92773c91..b2d6da9b 100644
--- a/converter/other/cameratopam/cameratopam.c
+++ b/converter/other/cameratopam/cameratopam.c
@@ -24,18 +24,13 @@
 #include <string.h>
 
 #ifdef __CYGWIN__
-#include <io.h>
+  #include <io.h>
 #endif
-#ifdef WIN32
-  #include <winsock2.h>
-  #pragma comment(lib, "ws2_32.lib")
-  #define strcasecmp stricmp
-  typedef __int64 INT64;
-  static bool const have64BitArithmetic = true;
-#else
+#if !defined(WIN32) || defined(__CYGWIN__)
   #include <unistd.h>
 #endif
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pam.h"
@@ -48,17 +43,6 @@
 #include "foveon.h"
 #include "dng.h"
 
-#if HAVE_INT64
-   typedef int64_t INT64;
-   static bool const have64BitArithmetic = true;
-#else
-   /* We define INT64 to something that lets the code compile, but we
-      should not execute any INT64 code, because it will get the wrong
-      result.  */
-   typedef int INT64;
-   static bool const have64BitArithmetic = false;
-#endif
-
 /*
    All global variables are defined here, and all functions that
    access them are prefixed with "CLASS".  Note that a thread-safe
diff --git a/converter/other/dithers.h b/converter/other/dithers.h
deleted file mode 100644
index 1ced833d..00000000
--- a/converter/other/dithers.h
+++ /dev/null
@@ -1,91 +0,0 @@
-#ifndef DITHERS_H_INCLUDED
-#define DITHERS_H_INCLUDED
-
-/*
-** dithers.h
-**
-** Here are some dithering matrices.  They are all taken from "Digital
-** Halftoning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6.
-*/
-
-
-#if 0
-/*
-** Order-6 ordered dithering matrix.  Note that smaller ordered dithers
-** have no advantage over larger ones, so use dither8 instead.
-*/
-static int dither6[8][8] = {
-  {  1, 59, 15, 55,  2, 56, 12, 52 },
-  { 33, 17, 47, 31, 34, 18, 44, 28 },
-  {  9, 49,  5, 63, 10, 50,  6, 60 },
-  { 41, 25, 37, 21, 42, 26, 38, 22 },
-  {  3, 57, 13, 53,  0, 58, 14, 54 },
-  { 35, 19, 45, 29, 32, 16, 46, 30 },
-  { 11, 51,  7, 61,  8, 48,  4, 62 },
-  { 43, 27, 39, 23, 40, 24, 36, 20 }
-  };
-#endif
-
-/* Order-8 ordered dithering matrix. */
-static int dither8[16][16] = {
-  {   1,235, 59,219, 15,231, 55,215,  2,232, 56,216, 12,228, 52,212},
-  { 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116},
-  {  33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244},
-  { 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92},
-  {   9,225, 49,209,  5,239, 63,223, 10,226, 50,210,  6,236, 60,220},
-  { 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124},
-  {  41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252},
-  { 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86},
-  {   3,233, 57,217, 13,229, 53,213,  0,234, 58,218, 14,230, 54,214},
-  { 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118},
-  {  35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246},
-  { 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94},
-  {  11,227, 51,211,  7,237, 61,221,  8,224, 48,208,  4,238, 62,222},
-  { 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126},
-  {  43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254},
-  { 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84} 
-};
-
-/* Order-3 clustered dithering matrix. */
-static int cluster3[6][6] = {
-  {  9,11,10, 8, 6, 7},
-  { 12,17,16, 5, 0, 1},
-  { 13,14,15, 4, 3, 2},
-  {  8, 6, 7, 9,11,10},
-  {  5, 0, 1,12,17,16},
-  {  4, 3, 2,13,14,15}
-};
-
-/* Order-4 clustered dithering matrix. */
-static int cluster4[8][8] = {
-  { 18,20,19,16,13,11,12,15},
-  { 27,28,29,22, 4, 3, 2, 9},
-  { 26,31,30,21, 5, 0, 1,10},
-  { 23,25,24,17, 8, 6, 7,14},
-  { 13,11,12,15,18,20,19,16},
-  {  4, 3, 2, 9,27,28,29,22},
-  {  5, 0, 1,10,26,31,30,21},
-  {  8, 6, 7,14,23,25,24,17}
-};
-
-/* Order-8 clustered dithering matrix. */
-static int cluster8[16][16] = {
-   { 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60},
-   { 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52},
-   { 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44},
-   { 88,110,123,124,125,118,107, 85, 39, 17,  4,  3,  2,  9, 20, 42},
-   { 89,111,122,127,126,117,106, 84, 38, 16,  5,  0,  1, 10, 21, 43},
-   { 79,102,119,121,120,113, 97, 82, 48, 25,  8,  6,  7, 14, 30, 45},
-   { 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53},
-   { 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61},
-   { 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67},
-   { 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75},
-   { 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83},
-   { 39, 17,  4,  3,  2,  9, 20, 42, 88,110,123,124,125,118,107, 85},
-   { 38, 16,  5,  0,  1, 10, 21, 43, 89,111,122,127,126,117,106, 84},
-   { 48, 25,  8,  6,  7, 14, 30, 45, 79,102,119,121,120,113, 97, 82},
-   { 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74},
-   { 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66}
-};
-
-#endif
diff --git a/converter/other/fiasco/Makefile b/converter/other/fiasco/Makefile
index 9f7310bd..16221d77 100644
--- a/converter/other/fiasco/Makefile
+++ b/converter/other/fiasco/Makefile
@@ -5,9 +5,9 @@ endif
 SUBDIR = converter/other/fiasco
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
-INCLUDES = \
+COMP_INCLUDES = \
 	-I$(SRCDIR)/$(SUBDIR)/codec -I$(SRCDIR)/$(SUBDIR)/input \
 	-I$(SRCDIR)/$(SUBDIR)/output -I$(SRCDIR)/$(SUBDIR)/lib \
 
@@ -32,7 +32,7 @@ MERGE_OBJECTS = $(BINARIES:%=%.o2) $(COMMON_OBJECTS)  $(FIASCOLIBS)
 
 SUBDIRS = codec input output lib
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 $(BINARIES):%:%.o $(COMMON_OBJECTS) $(FIASCOLIBS) $(NETPBMLIB) \
    $(LIBOPT)
diff --git a/converter/other/fiasco/codec/Makefile b/converter/other/fiasco/codec/Makefile
index 9a9d502a..e3b2a112 100644
--- a/converter/other/fiasco/codec/Makefile
+++ b/converter/other/fiasco/codec/Makefile
@@ -6,9 +6,9 @@ FIASCOSUBDIR = converter/other/fiasco
 SUBDIR = $(FIASCOSUBDIR)/codec
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+COMP_INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
 	 -I$(SRCDIR)/$(FIASCOSUBDIR)/input -I$(SRCDIR)/$(FIASCOSUBDIR)/output 
 
 OBJECTS =  approx.o bintree.o coder.o coeff.o \
@@ -19,7 +19,7 @@ MERGE_OBJECTS = $(OBJECTS)
 
 all: libfiasco_codec.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_codec.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/input/Makefile b/converter/other/fiasco/input/Makefile
index c01af772..2f8749f6 100644
--- a/converter/other/fiasco/input/Makefile
+++ b/converter/other/fiasco/input/Makefile
@@ -7,18 +7,18 @@ SUBDIR = $(FIASCOSUBDIR)/input
 BUILDDIR = ../../../..
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 OBJECTS =  basis.o matrices.o mc.o nd.o read.o tree.o weights.o
 
 MERGE_OBJECTS = $(OBJECTS)
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
-	   -I$(SRCDIR)/$(FIASCOSUBDIR)/codec
+COMP_INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+	        -I$(SRCDIR)/$(FIASCOSUBDIR)/codec
 
 all: libfiasco_input.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_input.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/lib/Makefile b/converter/other/fiasco/lib/Makefile
index 99d7c1d7..801fd24e 100644
--- a/converter/other/fiasco/lib/Makefile
+++ b/converter/other/fiasco/lib/Makefile
@@ -6,7 +6,7 @@ FIASCOSUBDIR = converter/other/fiasco
 SUBDIR = $(FIASCOSUBDIR)/lib
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 OBJECTS = \
   arith.o \
@@ -21,11 +21,11 @@ OBJECTS = \
 
 MERGE_OBJECTS = $(OBJECTS)
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR)
+COMP_INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR)
 
 all: libfiasco_lib.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_lib.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/lib/misc.c b/converter/other/fiasco/lib/misc.c
index 02a1314f..12b94e7a 100644
--- a/converter/other/fiasco/lib/misc.c
+++ b/converter/other/fiasco/lib/misc.c
@@ -432,38 +432,6 @@ Log2 (double x)
    return log (x) / 0.69314718;
 }
 
-#ifndef HAVE_STRCASECMP
-bool_t
-strcaseeq (const char *s1, const char *s2)
-/*
- *  Compare strings 's1' and 's2', ignoring  the  case of the characters.
- *
- *  Return value:
- *	TRUE if strings match, else FALSE
- */
-{
-   bool_t  matched;
-   char	  *ls1, *ls2, *ptr;
-
-   assert (s1 && s2);
-   
-   ls1 = strdup (s1);
-   ls2 = strdup (s2);
-   
-   for (ptr = ls1; *ptr; ptr++)
-      *ptr = tolower (*ptr);
-   for (ptr = ls2; *ptr; ptr++)
-      *ptr = tolower (*ptr);
-
-   matched = streq (ls1, ls2) ? YES : NO;
-
-   Free (ls1);
-   Free (ls2);
-   
-   return matched;
-}
-#endif /* not HAVE_STRCASECMP */
-
 real_t
 variance (const word_t *pixels, unsigned x0, unsigned y0,
 	  unsigned width, unsigned height, unsigned cols)
diff --git a/converter/other/fiasco/output/Makefile b/converter/other/fiasco/output/Makefile
index 3bdc4635..26b6d924 100644
--- a/converter/other/fiasco/output/Makefile
+++ b/converter/other/fiasco/output/Makefile
@@ -6,19 +6,20 @@ FIASCOSUBDIR = converter/other/fiasco
 SUBDIR = $(FIASCOSUBDIR)/output
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 OBJECTS =  matrices.o mc.o nd.o tree.o weights.o write.o
 
 MERGE_OBJECTS = $(OBJECTS)
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) \
-	   -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
-	   -I$(SRCDIR)/$(FIASCOSUBDIR)/codec 
+COMP_INCLUDES = \
+  -I$(SRCDIR)/$(FIASCOSUBDIR) \
+  -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+  -I$(SRCDIR)/$(FIASCOSUBDIR)/codec 
 
 all: libfiasco_output.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_output.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/params.c b/converter/other/fiasco/params.c
index 3d0a0252..7a302b82 100644
--- a/converter/other/fiasco/params.c
+++ b/converter/other/fiasco/params.c
@@ -15,7 +15,8 @@
  *  $State: Exp $
  */
 
-#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _BSD_SOURCE 1
+    /* Make sure strdup() is in string.h and strcaseeq() is in nstring.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include "config.h"
diff --git a/converter/other/fitstopnm.c b/converter/other/fitstopnm.c
index b4240d63..73564c4b 100644
--- a/converter/other/fitstopnm.c
+++ b/converter/other/fitstopnm.c
@@ -1,4 +1,4 @@
- /* fitstopnm.c - read a FITS file and produce a portable anymap
+ /* fitstopnm.c - read a FITS file and produce a PNM.
  **
  ** Copyright (C) 1989 by Jef Poskanzer.
  **
@@ -30,16 +30,118 @@
  ** disabled min max scanning when reading from stdin.
  */
 
+/*
+  The official specification of FITS format (which is for more than
+  just visual images) is at
+  ftp://legacy.gsfc.nasa.gov/fits_info/fits_office/fits_standard.pdf
+*/
+
 #include <string.h>
+#include <float.h>
 
+#include "pm_config.h"
 #include "pm_c_util.h"
+#include "mallocvar.h"
+#include "floatcode.h"
+#include "shhopt.h"
 #include "pnm.h"
 
-/* Do what you have to, to get a sensible value here.  This may not be so */
-/* portable as the rest of the program.  We want to use MAXFLOAT and */
-/* MAXDOUBLE, so you could define them manually if you have to. */
-/* #include <values.h> */
-#include <float.h>
+
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    unsigned int image;  /* zero if unspecified */
+    float max;
+    unsigned int maxSpec;
+    float min;
+    unsigned int minSpec;
+    unsigned int scanmax;
+    unsigned int printmax;
+    unsigned int noraw;
+        /* This is for backward compatibility only.  Use the common option
+           -plain now.  (pnm_init() processes -plain).
+        */
+    unsigned int verbose;
+    unsigned int omaxval;
+    unsigned int omaxvalSpec;
+};
+
+
+
+static void 
+parseCommandLine(int argc, char ** argv, 
+                 struct cmdlineInfo * const cmdlineP) {
+/* --------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+--------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+
+    unsigned int imageSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "image",    OPT_UINT,
+            &cmdlineP->image,   &imageSpec,                            0);
+    OPTENT3(0, "min",      OPT_FLOAT,
+            &cmdlineP->min,     &cmdlineP->minSpec,                    0);
+    OPTENT3(0, "max",      OPT_FLOAT,
+            &cmdlineP->max,     &cmdlineP->maxSpec,                    0);
+    OPTENT3(0, "scanmax",  OPT_FLAG,
+            NULL,               &cmdlineP->scanmax,                    0);
+    OPTENT3(0, "printmax", OPT_FLAG,
+            NULL,               &cmdlineP->printmax,                   0);
+    OPTENT3(0, "noraw",    OPT_FLAG,
+            NULL,               &cmdlineP->noraw,                      0);
+    OPTENT3(0, "verbose",  OPT_FLAG,
+            NULL,               &cmdlineP->verbose,                    0);
+    OPTENT3(0, "omaxval",  OPT_UINT,
+            &cmdlineP->omaxval, &cmdlineP->omaxvalSpec,                0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
+
+    /* Set some defaults the lazy way (using multiple setting of variables) */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (imageSpec) {
+        if (cmdlineP->image == 0)
+            pm_error("You may not specify zero for the image number.  "
+                     "Images are numbered starting at 1.");
+    } else
+        cmdlineP->image = 0;
+
+    if (cmdlineP->maxSpec && cmdlineP->minSpec) {
+        if (cmdlineP->max <= cmdlineP->min)
+            pm_error("-max must be greater than -min.  You specified "
+                     "-max=%f, -min=%f", cmdlineP->max, cmdlineP->min);
+    }
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%u).  The only non-option argument "
+                     "is the input file name.", argc-1);
+    }
+}
+
+
 
 struct FITS_Header {
   int simple;       /* basic format or not */
@@ -54,10 +156,262 @@ struct FITS_Header {
   double bscale;
 };
 
-static void read_fits_header ARGS(( FILE* fp, struct FITS_Header* hP ));
-static void read_card ARGS(( FILE* fp, char* buf ));
-static void read_val ARGS(( FILE* fp, int bitpix, double* vp ));
-     
+
+/* This code deals properly with integers, no matter what the byte order
+   or integer size of the host machine.  We handle sign extension manually to
+   prevent problems with signed/unsigned characters.  We read floating point
+   values properly only when the host architecture conforms to IEEE-754.  If
+   you need to tweak this code for other machines, you might want to get a
+   copy of the FITS documentation from nssdca.gsfc.nasa.gov
+*/
+
+static void
+readFitsChar(FILE *   const ifP,
+             double * const vP) {
+
+    /* 8 bit FITS integers are unsigned */
+
+    int const ich = getc(ifP);
+
+    if (ich == EOF)
+        pm_error("EOF / read error");
+    else
+        *vP = ich;
+}
+
+
+
+static void
+readFitsShort(FILE *   const ifP,
+              double * const vP) {
+
+    int ich;
+    int ival;
+    unsigned char c[8];
+
+    ich = getc(ifP);
+
+    if (ich == EOF)
+        pm_error("EOF / read error");
+
+    c[0] = ich;
+
+    ich = getc(ifP);
+
+    if (ich == EOF)
+        pm_error("EOF / read error");
+
+    c[1] = ich;
+
+    if (c[0] & 0x80)
+        ival = ~0xFFFF | c[0] << 8 | c[1];
+    else
+        ival = c[0] << 8 | c[1];
+
+    *vP = ival;
+}
+
+
+
+static void
+readFitsLong(FILE *   const ifP,
+             double * const vP) {
+
+    unsigned int i;
+    long int lval;
+    unsigned char c[4];
+
+    for (i = 0; i < 4; ++i) {
+        int const ich = getc(ifP);
+        if (ich == EOF)
+            pm_error("EOF / read error");
+        c[i] = ich;
+    }
+
+    if (c[0] & 0x80)
+        lval = ~0xFFFFFFFF | c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
+    else
+        lval = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3] << 0;
+
+    *vP = lval;
+}
+
+
+
+static void
+readFitsFloat(FILE *   const ifP,
+              double * const vP) {
+
+    unsigned int i;
+    pm_bigendFloat bigend;
+
+    for (i = 0; i < 4; ++i) {
+        int const ich = getc(ifP);
+        if (ich == EOF)
+            pm_error("EOF / read error");
+        bigend.bytes[i] = ich;
+    }
+
+    *vP = pm_floatFromBigendFloat(bigend);
+}
+
+
+
+static void
+readFitsDouble(FILE *   const ifP,
+               double * const vP) {
+
+    unsigned int i;
+    pm_bigendDouble bigend;
+
+    for (i = 0; i < 8; ++i) {
+        int const ich = getc(ifP);
+        if (ich == EOF)
+            pm_error("EOF / read error");
+        bigend.bytes[i] = ich;
+    }
+
+    *vP = pm_doubleFromBigendDouble(bigend);
+}
+
+
+
+static void
+readVal(FILE *   const ifP,
+        int      const bitpix,
+        double * const vP) {
+
+    switch (bitpix) {
+    case 8:
+        readFitsChar(ifP, vP);
+        break;
+
+    case 16:
+        readFitsShort(ifP, vP);
+        break;
+      
+    case 32:
+        readFitsLong(ifP, vP);
+        break;
+      
+    case -32:
+        readFitsFloat(ifP, vP);
+        break;
+      
+    case -64:
+        readFitsDouble(ifP, vP);
+        break;
+      
+    default:
+        pm_error("Strange bitpix value %d in readVal()", bitpix);
+    }
+}
+
+
+
+static void
+readCard(FILE * const ifP,
+         char * const buf) {
+
+    size_t bytesRead;
+
+    bytesRead = fread(buf, 1, 80, ifP);
+    if (bytesRead == 0)
+        pm_error("error reading header");
+}
+
+
+
+static void
+readFitsHeader(FILE *               const ifP,
+               struct FITS_Header * const hP) {
+
+    int seenEnd;
+  
+    seenEnd = 0;
+    /* Set defaults */
+    hP->simple  = 0;
+    hP->bzer    = 0.0;
+    hP->bscale  = 1.0;
+    hP->datamin = - DBL_MAX;
+    hP->datamax = DBL_MAX;
+  
+    while (!seenEnd) {
+        unsigned int i;
+        for (i = 0; i < 36; ++i) {
+            char buf[80];
+            char c;
+
+            readCard(ifP, buf);
+    
+            if (sscanf(buf, "SIMPLE = %c", &c) == 1) {
+                if (c == 'T' || c == 't')
+                    hP->simple = 1;
+            } else if (sscanf(buf, "BITPIX = %d", &(hP->bitpix)) == 1);
+            else if (sscanf(buf, "NAXIS = %d", &(hP->naxis)) == 1);
+            else if (sscanf(buf, "NAXIS1 = %d", &(hP->naxis1)) == 1);
+            else if (sscanf(buf, "NAXIS2 = %d", &(hP->naxis2)) == 1);
+            else if (sscanf(buf, "NAXIS3 = %d", &(hP->naxis3)) == 1);
+            else if (sscanf(buf, "DATAMIN = %lf", &(hP->datamin)) == 1);
+            else if (sscanf(buf, "DATAMAX = %lf", &(hP->datamax)) == 1);
+            else if (sscanf(buf, "BZERO = %lf", &(hP->bzer)) == 1);
+            else if (sscanf(buf, "BSCALE = %lf", &(hP->bscale)) == 1);
+            else if (strncmp(buf, "END ", 4 ) == 0) seenEnd = 1;
+        }
+    }
+}
+
+
+
+static void
+interpretPlanes(struct FITS_Header const fitsHeader,
+                unsigned int       const imageRequest,
+                bool               const verbose,
+                unsigned int *     const imageCountP,
+                bool *             const multiplaneP,
+                unsigned int *     const desiredImageP) {
+
+    if (fitsHeader.naxis == 2) {
+        *imageCountP   = 1;
+        *multiplaneP   = FALSE;
+        *desiredImageP = 1;
+    } else {
+        if (imageRequest) {
+            if (imageRequest > fitsHeader.naxis3)
+                pm_error("Only %u plane%s in this file.  "
+                         "You requested image %u", 
+                         fitsHeader.naxis3, fitsHeader.naxis3 > 1 ? "s" : "",
+                         imageRequest);
+            else {
+                *imageCountP   = fitsHeader.naxis3;
+                *multiplaneP   = FALSE;
+                *desiredImageP = imageRequest;
+            }
+        } else {
+            if (fitsHeader.naxis3 == 3) {
+                *imageCountP   = 1;
+                *multiplaneP   = TRUE;
+                *desiredImageP = 1;
+            } else if (fitsHeader.naxis3 > 1)
+                pm_error("This FITS file contains multiple (%u) images.  "
+                         "You must specify which one you want with a "
+                         "-image option.", fitsHeader.naxis3);
+            else {
+                *imageCountP   = fitsHeader.naxis3;
+                *multiplaneP   = FALSE;
+                *desiredImageP = 1;
+            }
+        }
+    }
+    if (verbose) {
+        
+        pm_message("FITS stream is %smultiplane", *multiplaneP ? "" : "not ");
+        pm_message("We will take image %u (1 is first) of %u "
+                   "in the FITS stream",
+                   *desiredImageP, *imageCountP);
+    }
+}
+
 
 
 static void
@@ -100,7 +454,7 @@ scanImageForMinMax(FILE *       const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                read_val(ifP, bitpix, &val);
+                readVal(ifP, bitpix, &val);
                 if (image == imagenum || multiplane ) {
                     dmax = MAX(dmax, val);
                     dmin = MIN(dmin, val);
@@ -170,344 +524,233 @@ computeMinMax(FILE *             const ifP,
 
 
 
-int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    int argn, imagenum, image, row;
-    register int col;
-    xelval maxval;
-    double val, frmin, frmax, scale, t;
-    double datamin, datamax;
-    int rows, cols, images, format;
-    struct FITS_Header h;
-    xel** pnmarray;
-    xelval tx, txv[4];
-    const char* fits_name;
-    const char* const usage = "[-image N] [-scanmax] [-printmax] [-min f] [-max f] [FITSfile]";
-
-    int doscan = 0;
-    int forceplain = 0;
-    int forcemin = 0;
-    int forcemax = 0;
-    int printmax = 0;
-    bool multiplane;
-  
-    pnm_init( &argc, argv );
-  
-    argn = 1;
-    imagenum = 0;
-  
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-image", 2 ) )
-        {
-            ++argn;
-            if ( argn == argc || sscanf( argv[argn], "%d", &imagenum ) != 1 )
-                pm_usage( usage );
-        }
-        else if ( pm_keymatch( argv[argn], "-max", 3 ) )
-        {
-            ++argn;
-            forcemax = 1;
-            if ( argn == argc || sscanf( argv[argn], "%lf", &frmax ) != 1 )
-                pm_usage( usage );
-        }
-        else if ( pm_keymatch( argv[argn], "-min", 3 ) )
-        {
-            ++argn;
-            forcemin = 1;
-            if ( argn == argc || sscanf( argv[argn], "%lf", &frmin ) != 1 )
-                pm_usage( usage );
-        }
-        else if ( pm_keymatch( argv[argn], "-scanmax", 2 ) )
-            doscan = 1;
-        else if ( pm_keymatch( argv[argn], "-noraw", 2 ) )
-            /* This is for backward compatibility only.  Use the common option
-               -plain now.  (pnm_init() processes -plain).
+static xelval
+determineMaxval(struct cmdlineInfo const cmdline,
+                struct FITS_Header const fitsHeader,
+                double             const datamax,
+                double             const datamin) {
+
+    xelval retval;
+                
+    if (cmdline.omaxvalSpec)
+        retval = cmdline.omaxval;
+    else {
+        if (fitsHeader.bitpix < 0) {
+            /* samples are floating point, which means the resolution
+               could be anything.  So we just pick a convenient maxval
+               of 255.  Before Netpbm 10.20 (January 2004), we did
+               maxval = max - min for floating point as well as
+               integer samples.
             */
-            forceplain = 1;
-        else if ( pm_keymatch( argv[argn], "-printmax", 2 ) )
-            printmax = 1;
-        else
-            pm_usage( usage );
-        ++argn;
-    }
-  
-    if ( argn < argc )
-    {
-        fits_name = argv[argn];
-        ++argn;
+            retval = 255;
+            if (cmdline.verbose)
+                pm_message("FITS image has floating point samples.  "
+                           "Using maxval = %u.", (unsigned int)retval);
+        } else {
+            retval = MAX(1, MIN(PNM_OVERALLMAXVAL, datamax - datamin));
+            if (cmdline.verbose)
+                pm_message("FITS image has samples in the range %d-%d.  "
+                           "Using maxval %u.",
+                           (int)(datamin+0.5), (int)(datamax+0.5),
+                           (unsigned int)retval);
+        }
     }
-    else
-        fits_name = "-";
-
-    if ( argn != argc )
-        pm_usage( usage );
-
-    ifp = pm_openr_seekable(fits_name);
-  
-    read_fits_header( ifp, &h );
-  
-    if ( ! h.simple )
-        pm_error( "FITS file is not in simple format, can't read" );
-    if ( h.naxis != 2 && h.naxis != 3 )
-        pm_message( "Warning: FITS file has %d axes", h.naxis );
-    cols = h.naxis1;
-    rows = h.naxis2;
-    if ( h.naxis == 2 )
-        images = imagenum = 1;
-    else
-        images = h.naxis3;
-    if ( imagenum > images )
-        pm_error( "only %d plane%s in this file", 
-                  images, images > 1 ? "s" : "" );
-    if ( images != 3 && images > 1 && imagenum == 0 )
-        pm_error( "need to specify a plane using the -imagenum flag" );
-
-    multiplane = ( images == 3 && imagenum == 0 );
+    return retval;
+}
 
-    computeMinMax(ifp, images, cols, rows, h, imagenum, multiplane,
-                  forcemin, forcemax, frmin, frmax,
-                  &datamin, &datamax);
 
-    if (h.bitpix < 0) {
-        /* samples are floating point, which means the resolution could be
-           anything.  So we just pick a convenient maxval of 255.  We should
-           have a program option to choose the maxval.  Before Netpbm 10.20
-           (January 2004), we did maxval = max - min for floating point as
-           well as integer samples.
-        */
-        maxval = 255;
-    } else
-        maxval = MAX(1, MIN(PNM_OVERALLMAXVAL, datamax - datamin));
 
-    if ( datamax - datamin == 0 )
-        scale = 1.0;
-    else
-        scale = maxval / ( datamax - datamin );
-
-    /* If printmax option is true, just print and exit. */
-    /* For use in shellscripts.  Ex:                    */
-    /* eval `fitstopnm -printmax $filename | \          */
-    /*   awk '{min = $1; max = $2}\                     */
-    /*         END {print "min=" min; " max=" max}'`    */
-    if (printmax) {
-        printf( "%f %f\n", datamin, datamax);
-        pm_close( ifp );
-        pm_close( stdout );
-        exit( 0 );
-    }
+static void
+convertPgmRaster(FILE *             const ifP,
+                 unsigned int       const cols,
+                 unsigned int       const rows,
+                 xelval             const maxval,
+                 unsigned int       const desiredImage,
+                 unsigned int       const imageCount,
+                 struct FITS_Header const fitsHdr,
+                 double             const scale,
+                 double             const datamin,
+                 xel **             const xels) {
+        
+    /* Note: the FITS specification does not give the association between
+       file position and image position (i.e. is the first pixel in the
+       file the top left, bottom left, etc.).  We use the common sense,
+       popular order of row major, top to bottom, left to right.  This
+       has been the case and accepted since 1989, but in 2008, we discovered
+       that Gimp and ImageMagick do bottom to top.
+    */
+    unsigned int image;
 
-    if (multiplane)
-        format = PPM_FORMAT;
-    else
-        format = PGM_FORMAT;
+    pm_message("Writing PPM file "
+               "(Probably not what you want - consider an -image option)");
 
-    pnmarray = pnm_allocarray( cols, rows );
-
-    switch( PNM_FORMAT_TYPE( format ))
-    {
-    case PGM_TYPE:
-        pm_message( "writing PGM file" );
-        for ( image = 1; image <= imagenum; ++image )
-        {
-            if ( image != imagenum )
-                pm_message( "skipping image plane %d of %d", image, images );
-            else if ( images > 1 )
-                pm_message( "reading image plane %d of %d", image, images );
-            for ( row = 0; row < rows; ++row)
-                for ( col = 0; col < cols; ++col )
+    for (image = 1; image <= desiredImage; ++image) {
+        unsigned int row;
+        if (image != desiredImage)
+            pm_message("skipping image plane %u of %u", image, imageCount);
+        else if (imageCount > 1)
+            pm_message("reading image plane %u of %u", image, imageCount);
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                double val;
+                readVal(ifP, fitsHdr.bitpix, &val);
                 {
-                    read_val (ifp, h.bitpix, &val);
-                    t = scale * ( val * h.bscale + h.bzer - datamin );
-                    tx = MAX( 0, MIN( t, maxval ) );
-                    if ( image == imagenum )
-                        PNM_ASSIGN1( pnmarray[row][col], tx );
+                    double const t = scale *
+                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                    xelval const tx = MAX(0, MIN(t, maxval));
+                    if (image == desiredImage)
+                        PNM_ASSIGN1(xels[row][col], tx);
                 }
+            }
         }
-        break;
-    case PPM_TYPE:
-        pm_message( "writing PPM file" );
-        for ( image = 1; image <= images; image++ )
-        {
-            pm_message( "reading image plane %d of %d", image, images );
-            for ( row = 0; row < rows; row++ )
-                for ( col = 0; col < cols; col++ )
+    } 
+}
+
+
+
+static void
+convertPpmRaster(FILE *             const ifP,
+                 unsigned int       const cols,
+                 unsigned int       const rows,
+                 xelval             const maxval,
+                 struct FITS_Header const fitsHdr,
+                 double             const scale,
+                 double             const datamin,
+                 xel **             const xels) {
+/*----------------------------------------------------------------------------
+   Read the FITS raster from file *ifP into xels[][].  Image dimensions
+   are 'cols' by 'rows'.  The FITS raster is 3 planes composing one
+   image: a red plane followed by a green plane followed by a blue plane.
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+
+    pm_message("writing PPM file");
+
+    for (plane = 0; plane < 3; ++plane) {
+        unsigned int row;
+        pm_message("reading image plane %u (%s)",
+                   plane, plane == 0 ? "red" : plane == 1 ? "green" : "blue");
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                double val;
+                readVal(ifP, fitsHdr.bitpix, &val);
                 {
-                    read_val (ifp, h.bitpix, &val);
-                    txv[1] = PPM_GETR( pnmarray[row][col] );
-                    txv[2] = PPM_GETG( pnmarray[row][col] );
-                    txv[3] = PPM_GETB( pnmarray[row][col] );
-                    t = scale * ( val * h.bscale + h.bzer - datamin );
-                    txv[image] = MAX( 0, MIN( t, maxval ));
-                    PPM_ASSIGN( pnmarray[row][col], txv[1], txv[2], txv[3] );
+                    double const t = scale *
+                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                    xelval const sample = MAX(0, MIN(t, maxval));
+
+                    switch (plane) {
+                    case 0: PPM_PUTR(xels[row][col], sample); break;
+                    case 1: PPM_PUTG(xels[row][col], sample); break;
+                    case 2: PPM_PUTB(xels[row][col], sample); break;
+                    }
                 }
+            }
         }
-        break;
-    default:
-        pm_error( "can't happen" );
-        break;
     }
-
-    pnm_writepnm( stdout, pnmarray, cols, rows, maxval, format, forceplain );
-    pnm_freearray( pnmarray, rows );
-
-    pm_close( ifp );
-    pm_close( stdout );
-  
-    exit( 0 );
 }
 
 
+
 static void
-swapbytes(void *       const p,
-          unsigned int const nbytes) {
-#if BYTE_ORDER == LITTLE_ENDIAN
-    unsigned char * const c = p;
-    unsigned int i;
-    for (i = 0; i < nbytes/2; ++i) {
-        unsigned char const orig = c[i];
-        c[i] = c[nbytes-(i+1)];
-        c[nbytes-(i+1)] = orig;
+convertRaster(FILE *             const ifP,
+              unsigned int       const cols,
+              unsigned int       const rows,
+              xelval             const maxval,
+              bool               const forceplain,
+              bool               const multiplane,
+              unsigned int       const desiredImage,
+              unsigned int       const imageCount,
+              struct FITS_Header const fitsHdr,
+              double             const scale,
+              double             const datamin) {
+
+    xel ** xels;
+    int format;
+
+    xels = pnm_allocarray(cols, rows);
+
+    if (multiplane) {
+        format = PPM_FORMAT;
+        convertPpmRaster(ifP, cols, rows, maxval, fitsHdr, scale, datamin,
+                         xels);
+    } else {
+        format = PGM_FORMAT;
+        convertPgmRaster(ifP, cols, rows, maxval,
+                         desiredImage, imageCount, fitsHdr, scale, datamin,
+                         xels);
     }
-#endif
+    pnm_writepnm(stdout, xels, cols, rows, maxval, format, forceplain);
+    pnm_freearray(xels, rows);
 }
 
 
-/*
- ** This code will deal properly with integers, no matter what the byte order
- ** or integer size of the host machine.  Sign extension is handled manually
- ** to prevent problems with signed/unsigned characters.  Floating point
- ** values will only be read properly when the host architecture is IEEE-754
- ** conformant.  If you need to tweak this code for other machines, you might
- ** want to snag a copy of the FITS documentation from nssdca.gsfc.nasa.gov
- */
 
-static void
-read_val (fp, bitpix, vp)
-    FILE *fp;
-    int bitpix;
-    double *vp;
+int
+main(int argc, char * argv[]) {
 
-{
-    int i, ich, ival;
-    long int lval;
-    unsigned char c[8];
-  
-    switch ( bitpix )
-    {
-        /* 8 bit FITS integers are unsigned */
-    case 8:
-        ich = getc( fp );
-        if ( ich == EOF )
-            pm_error( "EOF / read error" );
-        *vp = ich;
-        break;
-      
-    case 16:
-        ich = getc( fp );
-        if ( ich == EOF )
-            pm_error( "EOF / read error" );
-        c[0] = ich;
-        ich = getc( fp );
-        if ( ich == EOF )
-            pm_error( "EOF / read error" );
-        c[1] = ich;
-        if (c[0] & 0x80)
-            ival = ~0xFFFF | c[0]<<8 | c[1];
-        else
-            ival = c[0]<<8 | c[1];
-        *vp = ival;
-        break;
-      
-    case 32:
-        for (i=0; i<4; i++) {
-            ich = getc( fp );
-            if ( ich == EOF )
-                pm_error( "EOF / read error" );
-            c[i] = ich;
-        }
-        if (c[0] & 0x80)
-            lval = ~0xFFFFFFFF |
-                c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
-        else
-            lval = c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
-        *vp = lval;
-        break;
-      
-    case -32:
-        for (i=0; i<4; i++) {
-            ich = getc( fp );
-            if ( ich == EOF )
-                pm_error( "EOF / read error" );
-            c[i] = ich;
-        }
-        swapbytes(c, 4);
-        *vp = *( (float *) c);
-        break;
-      
-    case -64:
-        for (i=0; i<8; i++) {
-            ich = getc( fp );
-            if ( ich == EOF )
-                pm_error( "EOF / read error" );
-            c[i] = ich;
-        }
-        swapbytes(c, 8);
-        *vp = *( (double *) c);
-        break;
-      
-    default:
-        pm_error( "Strange bitpix in read_value" );
-    }
-}
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    unsigned int cols, rows;
+    xelval maxval;
+    double scale;
+    double datamin, datamax;
+    struct FITS_Header fitsHeader;
 
-static void
-read_fits_header( fp, hP )
-    FILE* fp;
-    struct FITS_Header* hP;
-{
-    char buf[80];
-    int seen_end;
-    int i;
-    char c;
+    unsigned int imageCount;
+    unsigned int desiredImage;
+        /* Plane number (starting at one) of plane that contains the image
+           we want.
+        */
+    bool multiplane;
+        /* This is a one-image multiplane stream; 'desiredImage'
+           is undefined
+        */
   
-    seen_end = 0;
-    hP->simple = 0;
-    hP->bzer = 0.0;
-    hP->bscale = 1.0;
-    hP->datamin = - DBL_MAX;
-    hP->datamax = DBL_MAX;
+    pnm_init( &argc, argv );
   
-    while ( ! seen_end )
-        for ( i = 0; i < 36; ++i )
-        {
-            read_card( fp, buf );
-    
-            if ( sscanf( buf, "SIMPLE = %c", &c ) == 1 )
-            {
-                if ( c == 'T' || c == 't' )
-                    hP->simple = 1;
-            }
-            else if ( sscanf( buf, "BITPIX = %d", &(hP->bitpix) ) == 1 );
-            else if ( sscanf( buf, "NAXIS = %d", &(hP->naxis) ) == 1 );
-            else if ( sscanf( buf, "NAXIS1 = %d", &(hP->naxis1) ) == 1 );
-            else if ( sscanf( buf, "NAXIS2 = %d", &(hP->naxis2) ) == 1 );
-            else if ( sscanf( buf, "NAXIS3 = %d", &(hP->naxis3) ) == 1 );
-            else if ( sscanf( buf, "DATAMIN = %lf", &(hP->datamin) ) == 1 );
-            else if ( sscanf( buf, "DATAMAX = %lf", &(hP->datamax) ) == 1 );
-            else if ( sscanf( buf, "BZERO = %lf", &(hP->bzer) ) == 1 );
-            else if ( sscanf( buf, "BSCALE = %lf", &(hP->bscale) ) == 1 );
-            else if ( strncmp( buf, "END ", 4 ) == 0 ) seen_end = 1;
-        }
-}
+    parseCommandLine(argc, argv, &cmdline);
 
-static void
-read_card( fp, buf )
-    FILE* fp;
-    char* buf;
-{
-    if ( fread( buf, 1, 80, fp ) == 0 )
-        pm_error( "error reading header" );
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readFitsHeader(ifP, &fitsHeader);
+  
+    if (!fitsHeader.simple)
+        pm_error("FITS file is not in simple format, can't read");
+
+    if (fitsHeader.naxis != 2 && fitsHeader.naxis != 3)
+        pm_message("Warning: FITS file has %u axes", fitsHeader.naxis);
+
+    cols = fitsHeader.naxis1;
+    rows = fitsHeader.naxis2;
+
+    interpretPlanes(fitsHeader, cmdline.image, cmdline.verbose,
+                    &imageCount, &multiplane, &desiredImage);
+
+    computeMinMax(ifP, imageCount, cols, rows, fitsHeader,
+                  desiredImage, multiplane,
+                  cmdline.minSpec, cmdline.maxSpec,
+                  cmdline.min, cmdline.max,
+                  &datamin, &datamax);
+
+    maxval = determineMaxval(cmdline, fitsHeader, datamax, datamin);
+
+    if (datamax - datamin == 0)
+        scale = 1.0;
+    else
+        scale = maxval / (datamax - datamin);
+
+    if (cmdline.printmax)
+        printf("%f %f\n", datamin, datamax);
+    else
+        convertRaster(ifP, cols, rows, maxval, cmdline.noraw,
+                      multiplane, desiredImage, imageCount,
+                      fitsHeader, scale, datamin);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
 }
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index 7337960c..4b8b0487 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -23,10 +23,11 @@
 #include <string.h>
 #include <assert.h>
 
-#include "pnm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "pnm.h"
 
 #define GIFMAXVAL 255
 #define MAXCOLORMAPSIZE 256
@@ -41,6 +42,13 @@
 #define LOCALCOLORMAP  0x80
 #define BitSet(byte, bit)      (((byte) & (bit)) == (bit))
 
+#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN)
+  /* make sure (BYTE_ORDER == LITTLE_ENDIAN) is FALSE */ 
+  #define BYTE_ORDER    0
+  #define LITTLE_ENDIAN 1
+#endif
+
+
 static __inline__ bool
 ReadOK(FILE *          const fileP,
        unsigned char * const buffer,
@@ -54,6 +62,33 @@ ReadOK(FILE *          const fileP,
 }
 
 
+
+static void
+readFile(FILE *          const ifP,
+         unsigned char * const buffer,
+         size_t          const len,
+         const char **   const errorP) {
+
+    size_t bytesRead;
+
+    bytesRead = fread(buffer, len, 1, ifP);
+
+    if (bytesRead == len)
+        *errorP = NULL;
+    else {
+        if (ferror(ifP))
+            asprintfN(errorP, "Error reading file.  errno=%d (%s)",
+                      errno, strerror(errno));
+        else if (feof(ifP))
+            asprintfN(errorP, "End of file encountered");
+        else
+            asprintfN(errorP, "Short read -- %u bytes of %u",
+                              (unsigned)bytesRead, (unsigned)len);
+    }
+}
+
+
+
 #define LM_to_uint(a,b)                        (((b)<<8)|(a))
 
 static int const maxnum_lzwCode = (1<<MAX_LZW_BITS);
@@ -72,6 +107,7 @@ struct cmdlineInfo {
         */
     const char * alpha_filename;
     unsigned int quitearly;
+    unsigned int repair;
 };
 
 
@@ -101,8 +137,10 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->verbose,         0);
     OPTENT3(0, "comments",    OPT_FLAG, NULL,
             &cmdlineP->comments,        0);
-    OPTENT3(0, "quitearly",    OPT_FLAG, NULL,
+    OPTENT3(0, "quitearly",   OPT_FLAG, NULL,
             &cmdlineP->quitearly,       0);
+    OPTENT3(0, "repair",      OPT_FLAG, NULL,
+            &cmdlineP->repair,          0);
     OPTENT3(0, "image",       OPT_STRING, &image,
             &imageSpec,                 0);
     OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alpha_filename, 
@@ -232,13 +270,15 @@ readColorMap(FILE *ifP, const int colormapsize,
 
 static bool zeroDataBlock = FALSE;
     /* the most recently read DataBlock was an EOD marker, i.e. had
-       zero length */
+       zero length
+    */
 
 static void
 getDataBlock(FILE *          const ifP, 
              unsigned char * const buf, 
              bool *          const eofP,
-             unsigned int *  const lengthP) {
+             unsigned int *  const lengthP,
+             const char **   const errorP) {
 /*----------------------------------------------------------------------------
    Read a DataBlock from file 'ifP', return it at 'buf'.
 
@@ -258,10 +298,11 @@ getDataBlock(FILE *          const ifP,
     unsigned char count;
     bool successfulRead;
     
-    long const pos=ftell(ifP);
+    long const pos = ftell(ifP);
     successfulRead = ReadOK(ifP, &count, 1);
     if (!successfulRead) {
         pm_message("EOF or error in reading DataBlock size from file" );
+        *errorP = NULL;
         *eofP = TRUE;
         *lengthP = 0;
     } else {
@@ -270,17 +311,21 @@ getDataBlock(FILE *          const ifP,
         *eofP = FALSE;
         *lengthP = count;
 
-        if (count == 0) 
+        if (count == 0) {
+            *errorP = NULL;
             zeroDataBlock = TRUE;
-        else {
+        } else {
             bool successfulRead;
 
             zeroDataBlock = FALSE;
             successfulRead = ReadOK(ifP, buf, count); 
-            
-            if (!successfulRead) 
-                pm_error("EOF or error reading data portion of %d byte "
-                         "DataBlock from file", count);
+
+            if (successfulRead) 
+                *errorP = NULL;
+            else
+                asprintfN(errorP,
+                          "EOF or error reading data portion of %u byte "
+                          "DataBlock from file", count);
         }
     }
 }
@@ -303,14 +348,15 @@ readThroughEod(FILE * const ifP) {
     while (!eod) {
         bool eof;
         unsigned int count;
+        const char * error;
 
-        getDataBlock(ifP, buf, &eof, &count);
-        if (eof)
+        getDataBlock(ifP, buf, &eof, &count, &error);
+        if (error || eof)
             pm_message("EOF encountered before EOD marker.  The GIF "
                        "file is malformed, but we are proceeding "
                        "anyway as if an EOD marker were at the end "
                        "of the file.");
-        if (eof || count == 0)
+        if (error || eof || count == 0)
             eod = TRUE;
     }
 }
@@ -334,7 +380,11 @@ doCommentExtension(FILE * const ifP) {
     done = FALSE;
     while (!done) {
         bool eof;
-        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen); 
+        const char * error;
+        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen, &error); 
+        if (error)
+            pm_error("Error reading a data block in a comment extension.  %s",
+                     error);
         if (blocklen == 0 || eof)
             done = TRUE;
         else {
@@ -355,8 +405,11 @@ doGraphicControlExtension(FILE *         const ifP,
     bool eof;
     unsigned int length;
     static unsigned char buf[256];
+    const char * error;
 
-    getDataBlock(ifP, buf, &eof, &length);
+    getDataBlock(ifP, buf, &eof, &length, &error);
+    if (error)
+        pm_error("Error reading 1st data block of Graphic Control Extension");
     if (eof)
         pm_error("EOF/error encountered reading "
                  "1st DataBlock of Graphic Control Extension.");
@@ -451,28 +504,16 @@ struct getCodeState {
         */
     bool streamExhausted;
         /* The last time we read from the input stream, we got an EOD marker
-           or EOF
+           or EOF or an error that prevents further reading of the stream.
         */
 };
 
 
 
 static void
-initGetCode(struct getCodeState * const getCodeStateP) {
-    
-    /* Fake a previous data block */
-    getCodeStateP->buf[0] = 0;
-    getCodeStateP->buf[1] = 0;
-    getCodeStateP->bufCount = 2;
-    getCodeStateP->curbit = getCodeStateP->bufCount * 8;
-    getCodeStateP->streamExhausted = FALSE;
-}
-
-
-
-static void
-getAnotherBlock(FILE * const ifP, 
-                struct getCodeState * const gsP) {
+getAnotherBlock(FILE *                const ifP, 
+                struct getCodeState * const gsP,
+                const char **         const errorP) {
 
     unsigned int count;
     unsigned int assumed_count;
@@ -490,98 +531,145 @@ getAnotherBlock(FILE * const ifP,
     gsP->bufCount = 2;
         
     /* Add the next block to the buffer */
-    getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count);
-    if (eof) {
-        pm_message("EOF encountered in image "
-                   "before EOD marker.  The GIF "
-                   "file is malformed, but we are proceeding "
-                   "anyway as if an EOD marker were at the end "
-                   "of the file.");
-        assumed_count = 0;
-    } else
-        assumed_count = count;
-
-    gsP->streamExhausted = (assumed_count == 0);
+    getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count, errorP);
+    if (*errorP)
+        gsP->streamExhausted = TRUE;
+    else {
+        if (eof) {
+            pm_message("EOF encountered in image "
+                       "before EOD marker.  The GIF "
+                       "file is malformed, but we are proceeding "
+                       "anyway as if an EOD marker were at the end "
+                       "of the file.");
+            assumed_count = 0;
+        } else
+            assumed_count = count;
 
-    gsP->bufCount += assumed_count;
+        gsP->streamExhausted = (assumed_count == 0);
+        
+        gsP->bufCount += assumed_count;
+    }
 }
 
 
 
+static struct getCodeState getCodeState;
+
 static void
-doGetCode(FILE *                const ifP, 
-          int                   const codeSize,
-          struct getCodeState * const gsP,
-          int *                 const retvalP) {
+getCode_init(struct getCodeState * const getCodeStateP) {
+    
+    /* Fake a previous data block */
+    getCodeStateP->buf[0] = 0;
+    getCodeStateP->buf[1] = 0;
+    getCodeStateP->bufCount = 2;
+    getCodeStateP->curbit = getCodeStateP->bufCount * 8;
+    getCodeStateP->streamExhausted = FALSE;
+}
 
-    while (gsP->curbit + codeSize > gsP->bufCount * 8 &&
-           !gsP->streamExhausted) 
-        /* Not enough left in buffer to satisfy request.  Get the next
-           data block into the buffer.
 
-           Note that a data block may be as small as one byte, so we may need
-           to do this multiple times to get the full code.  (This probably
-           never happens in practice).
-        */
-        getAnotherBlock(ifP, gsP);
 
-    if ((gsP->curbit+codeSize) > gsP->bufCount*8) {
-        /* If the buffer still doesn't have enough bits in it, that means
-           there were no data blocks left to read.
+static unsigned int
+bitsOfLeBuffer(const unsigned char * const buf,
+               unsigned int          const start,
+               unsigned int          const len) {
+/*----------------------------------------------------------------------------
+   Return a string of 'len' bits (up to 16) starting at bit 'start' of buffer
+   buf[].
+
+   In the buffer, the bits are numbered Intel-style, with the first bit of a
+   byte being the least significant bit.  So bit 3 is the "16" bit of the
+   first byte of buf[].
+
+   We return the string as an integer such that its pure binary encoding with
+   the bits numbered Intel-style is the string.  E.g. the string 0,1,1 
+   yields six.
+-----------------------------------------------------------------------------*/
+    uint32_t codeBlock;
+        /* The 3 whole bytes of the buffer that contain the requested
+           bit string
         */
-        *retvalP = -1;  /* EOF */
-
-        {
-            int const bitsUnused = gsP->bufCount*8 - gsP->curbit;
-            if (bitsUnused > 0)
-                pm_message("Stream ends with a partial code "
-                           "(%d bits left in file; "
-                           "expected a %d bit code).  Ignoring.",
-                           bitsUnused, codeSize);
-        }
-    } else {
-        int i, j;
-        int code;
-        unsigned char * const buf = gsP->buf;
-
-        code = 0;  /* initial value */
-        for (i = gsP->curbit, j = 0; j < codeSize; ++i, ++j)
-            code |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
-        gsP->curbit += codeSize;
-        *retvalP = code;
-    }
+
+    assert(len <= 16);
+
+    if (BYTE_ORDER == LITTLE_ENDIAN)
+        /* Fast path */
+        codeBlock = *(uint32_t *) & buf[start/8];
+    else
+        /* logic works for little endian too */
+        codeBlock =
+            (buf[start/8+0] <<  0) |
+            (buf[start/8+1] <<  8) |
+            (buf[start/8+2] << 16);
+            
+    return (unsigned int) 
+        (codeBlock >> (start % 8)) & ((1 << len) - 1);
 }
 
 
 
-static int
-getCode(FILE * const ifP, 
-        int    const codeSize, 
-        bool   const init)
-{
+static void
+getCode_get(struct getCodeState * const gsP,
+            FILE *                const ifP, 
+            int                   const codeSize,
+            bool *                const eofP,
+            unsigned int *        const codeP,
+            const char **         const errorP) {
 /*----------------------------------------------------------------------------
-   If 'init', initialize the code getter.
+  Read and return the next lzw code from the file *ifP.
+
+  'codeSize' is the number of bits in the code we are to get.
 
-   Otherwise, read and return the next lzw code from the file *ifP.
+  Return *eofP == TRUE iff we hit the end of the stream.  That means a legal
+  end of stream, marked by an EOD marker, not just end of file.  An end of
+  file in the middle of the GIF stream is an error.
 
-   'codeSize' is the number of bits in the code we are to get.
+  If there are bits left in the stream, but not 'codeSize' of them, we
+  call that a success with *eofP == TRUE.
 
-   Return -1 instead of a code if we encounter the end of the file.
+  Return the code read (assuming *eofP == FALSE and *errorP == NULL)
+  as *codeP.
 -----------------------------------------------------------------------------*/
-    static struct getCodeState getCodeState;
 
-    int retval;
+    *errorP = NULL;
+
+    while (gsP->curbit + codeSize > gsP->bufCount * 8 &&
+           !gsP->streamExhausted && !*errorP) 
+        /* Not enough left in buffer to satisfy request.  Get the next
+           data block into the buffer.
+
+           Note that a data block may be as small as one byte, so we may need
+           to do this multiple times to get the full code.  (This probably
+           never happens in practice).
+        */
+        getAnotherBlock(ifP, gsP, errorP);
 
-    if (init) {
-        initGetCode(&getCodeState);
-        retval = 0;
-    } else 
-        doGetCode(ifP, codeSize, &getCodeState, &retval);
+    if (!*errorP) {
+        if (gsP->curbit + codeSize > gsP->bufCount * 8) {
+            /* The buffer still doesn't have enough bits in it; that means
+               there were no data blocks left to read.
+            */
+            *eofP = TRUE;
+
+            {
+                int const bitsUnused = gsP->bufCount * 8 - gsP->curbit;
+                if (bitsUnused > 0)
+                    pm_message("Stream ends with a partial code "
+                               "(%d bits left in file; "
+                               "expected a %d bit code).  Ignoring.",
+                               bitsUnused, codeSize);
+            }
+        } else {
+            *codeP = bitsOfLeBuffer(gsP->buf, gsP->curbit, codeSize);
 
-    return retval;
+            gsP->curbit += codeSize;
+            *eofP = FALSE;
+        }
+    }
 }
 
 
+
+
 struct stack {
     /* Stack grows from low addresses to high addresses */
     int * stack;  /* malloc'ed array */
@@ -638,6 +726,7 @@ termStack(struct stack * const stackP) {
     stackP->stack = NULL;
 }
 
+    
 
 /*----------------------------------------------------------------------------
    Some notes on LZW.
@@ -657,7 +746,7 @@ termStack(struct stack * const stackP) {
    max_dataVal.  The first byte in the stream tells you what dataWidth
    is.
 
-   LZW codes 0 - max_dataVal are direct codes.  Each on represents
+   LZW codes 0 - max_dataVal are direct codes.  Each one represents
    the true data element whose value is that of the LZW code itself.
    No decompression is required.
 
@@ -694,7 +783,7 @@ struct decompressor {
         */
     int      next_tableSlot;
         /* Index in the code translation table of the next free entry */
-    int      firstcode;
+    unsigned int firstcode;
         /* This is always a true data element code */
     int      prevcode;
         /* The code just before, in the image, the one we're processing now */
@@ -759,7 +848,7 @@ lzwInit(struct decompressor * const decompP,
     }
     resetDecompressor(decompP);
 
-    getCode(decompP->ifP, 0, TRUE);
+    getCode_init(&getCodeState);
     
     decompP->fresh = TRUE;
     
@@ -779,7 +868,7 @@ lzwTerm(struct decompressor * const decompP) {
 static void
 expandCodeOntoStack(struct decompressor * const decompP,
                     int                   const incode,
-                    bool *                const errorP) {
+                    const char **         const errorP) {
 /*----------------------------------------------------------------------------
    'incode' is an LZW string code.  It represents a string of true data
    elements, as defined by the string translation table in *decompP.
@@ -790,12 +879,13 @@ expandCodeOntoStack(struct decompressor * const decompP,
    Also add to the translation table where appropriate.
 
    Iff the translation table contains a cycle (which means the LZW stream
-   from which it was built is invalid), return *errorP == TRUE.
+   from which it was built is invalid), fail (return text explanation
+   as *errorP).
 -----------------------------------------------------------------------------*/
     int code;
-    bool error;
+    const char * error;
 
-    error = FALSE;
+    error = NULL; /* Initial value */
 
     if (incode < decompP->next_tableSlot) 
         code = incode;
@@ -810,15 +900,15 @@ expandCodeOntoStack(struct decompressor * const decompP,
            represents and push it onto the code stack so the
            leftmost code is on top.  Set decompP->firstcode to the
            first (leftmost) code in that string.
-            */
+        */
 
         unsigned int stringCount;
         stringCount = 0;
 
         while (code > decompP->max_dataVal && !error) {
             if (stringCount > maxnum_lzwCode) {
-                pm_message("Error in GIF image: contains LZW string loop");
-                error = TRUE;
+                asprintfN(&error,
+                          "Error in GIF image: contains LZW string loop");
             } else {
                 ++stringCount;
                 pushStack(&decompP->stack, decompP->table[1][code]);
@@ -847,78 +937,106 @@ expandCodeOntoStack(struct decompressor * const decompP,
         }
     }
 
-    decompP->prevcode = incode;
     *errorP = error;
+
+    decompP->prevcode = incode;
 }
 
 
 
-static int
-lzwReadByte(struct decompressor * const decompP) {
+static void
+lzwReadByteFresh(struct getCodeState * const getCodeStateP,
+                 struct decompressor * const decompP,
+                 bool *                const endOfImageP,
+                 unsigned int *        const dataReadP,
+                 const char **         const errorP) {
+                     
+    /* Read off all initial clear codes, read the first non-clear code,
+       and return it.  There are no strings in the table yet, so the next
+       code must be a direct true data code.
+    */
+    bool eof;
+    do {
+        getCode_get(getCodeStateP, decompP->ifP, decompP->codeSize,
+                    &eof, &decompP->firstcode, errorP);
+        decompP->prevcode = decompP->firstcode;
+    } while (decompP->firstcode == decompP->clear_code && !*errorP && !eof);
+
+    if (!*errorP) {
+        if (eof)
+            *endOfImageP = TRUE;
+        else if (decompP->firstcode == decompP->end_code) {
+            if (!zeroDataBlock)
+                readThroughEod(decompP->ifP);
+            *endOfImageP = TRUE;
+        } else {
+            *endOfImageP = FALSE;
+            *dataReadP = decompP->firstcode;
+        }
+    }
+}
+
+
+
+static void
+lzwReadByte(struct decompressor * const decompP,
+            unsigned int *        const dataReadP,
+            bool *                const endOfImageP,
+            const char **         const errorP) {
 /*----------------------------------------------------------------------------
   Return the next data element of the decompressed image.  In the context
   of a GIF, a data element is the color table index of one pixel.
 
-  We read and return the next byte of the decompressed image, or:
+  We read and return the next byte of the decompressed image.
 
-    Return -1 if we hit EOF prematurely (i.e. before an "end" code.  We
-    forgive the case that the "end" code is followed by EOF instead of
-    an EOD marker (zero length DataBlock)).
+  If we can't, because the stream is too corrupted to make sense out of
+  it or the stream ends, we fail (return text description of why as
+  *errorP).
 
-    Return -2 if there are no more bytes in the image.  In that case,
-    make sure the file is positioned immediately after the image (i.e.
-    after the EOD marker that marks the end of the image or EOF).
+  We forgive the case that the "end" code is the end of the stream --
+  not followed by an EOD marker (zero length DataBlock).
 
-    Return -3 if we encounter errors in the LZW stream.
+  Iff we can't read a byte because we've hit the end of the image,
+  we return *endOfImageP = true.
 -----------------------------------------------------------------------------*/
-    int retval;
-
-    if (!stackIsEmpty(&decompP->stack))
-        retval = popStack(&decompP->stack);
-    else if (decompP->fresh) {
+    if (!stackIsEmpty(&decompP->stack)) {
+        *errorP = NULL;
+        *endOfImageP = FALSE;
+        *dataReadP = popStack(&decompP->stack);
+    } else if (decompP->fresh) {
         decompP->fresh = FALSE;
-        /* Read off all initial clear codes, read the first non-clear code,
-           and return it.  There are no strings in the table yet, so the next
-           code must be a direct true data code.
-        */
-        do {
-            decompP->firstcode =
-                getCode(decompP->ifP, decompP->codeSize, FALSE);
-            decompP->prevcode = decompP->firstcode;
-        } while (decompP->firstcode == decompP->clear_code);
-        if (decompP->firstcode == decompP->end_code) {
-            if (!zeroDataBlock)
-                readThroughEod(decompP->ifP);
-            retval = -2;
-        } else
-            retval = decompP->firstcode;
+
+        lzwReadByteFresh(&getCodeState, decompP, endOfImageP, dataReadP,
+                         errorP);
     } else {
-        int code;
-        code = getCode(decompP->ifP, decompP->codeSize, FALSE);
-        if (code == -1)
-            retval = -1;
-        else {
-            assert(code >= 0);  /* -1 is only possible error return */
-            if (code == decompP->clear_code) {
-                resetDecompressor(decompP);
-                retval = lzwReadByte(decompP);
-            } else {
-                if (code == decompP->end_code) {
-                    if (!zeroDataBlock)
-                        readThroughEod(decompP->ifP);
-                    retval = -2;
+        unsigned int code;
+        bool eof;
+        getCode_get(&getCodeState, decompP->ifP, decompP->codeSize,
+                    &eof, &code, errorP);
+        if (!*errorP) {
+            if (eof)
+                asprintfN(errorP,
+                          "Premature end of file; no proper GIF closing");
+            else {
+                if (code == decompP->clear_code) {
+                    resetDecompressor(decompP);
+                    lzwReadByte(decompP, dataReadP, endOfImageP, errorP);
                 } else {
-                    bool error;
-                    expandCodeOntoStack(decompP, code, &error);
-                    if (error)
-                        retval = -3;
-                    else
-                        retval = popStack(&decompP->stack);
+                    if (code == decompP->end_code) {
+                        if (!zeroDataBlock)
+                            readThroughEod(decompP->ifP);
+                        *endOfImageP = TRUE;
+                        *errorP = NULL;
+                    } else {
+                        *endOfImageP = FALSE;
+                        expandCodeOntoStack(decompP, code, errorP);
+                        if (!*errorP)
+                            *dataReadP = popStack(&decompP->stack);
+                    }
                 }
             }
         }
     }
-    return retval;
 }
 
 
@@ -1027,6 +1145,124 @@ addPixelToRaster(unsigned int       const cmapIndex,
 
 
 static void
+verifyPixelRead(bool          const endOfImage,
+                const char *  const readError,
+                unsigned int  const cols,
+                unsigned int  const rows,
+                unsigned int  const failedRowNum,
+                const char ** const errorP) {
+
+    if (readError)
+        *errorP = strdup(readError);
+    else {
+        if (endOfImage)
+            asprintfN(errorP,
+                      "Error in GIF image: Not enough raster data to fill "
+                      "%u x %u dimensions.  Ran out of raster data in "
+                      "row %u.  The image has proper ending sequence, so "
+                      "this is not just a truncated file.",
+                      cols, rows, failedRowNum);
+        else
+            *errorP = NULL;
+    }
+}
+
+
+
+static void
+readRaster(struct decompressor * const decompP,
+           xel **                const xels, 
+           unsigned int          const cols,
+           unsigned int          const rows,
+           gifColorMap                 cmap, 
+           unsigned int          const cmapSize,
+           bool                  const interlace,
+           int                   const transparentIndex,
+           bit **                const alphabits,
+           bool                  const tolerateBadInput) {
+                   
+    struct pnmBuffer pnmBuffer;
+    enum pass pass;
+    bool fillingMissingPixels;
+
+    pass = MULT8PLUS0;
+    pnmBuffer.xels = xels;
+    pnmBuffer.col  = 0;
+    pnmBuffer.row  = 0;
+    fillingMissingPixels = false;  /* initial value */
+
+    while (pnmBuffer.row < rows) {
+        unsigned int colorIndex;
+
+        if (fillingMissingPixels)
+            colorIndex = 0;
+        else {
+            const char * error;
+
+            const char * readError;
+            unsigned int readColorIndex;
+            bool endOfImage;
+
+            lzwReadByte(decompP, &readColorIndex, &endOfImage, &readError);
+
+            verifyPixelRead(endOfImage, readError, cols, rows, pnmBuffer.row,
+                            &error);
+
+            if (readError)
+                strfree(readError);
+
+            if (error) {
+                if (tolerateBadInput) {
+                    pm_message("WARNING: %s.  "
+                               "Filling bottom %u rows with arbitrary color",
+                               error, rows - pnmBuffer.row);
+                    fillingMissingPixels = true;
+                } else
+                    pm_error("Unable to read input image.  %s.  Use the "
+                             "-repair option to try to salvage some of "
+                             "the image",
+                             error);
+
+                colorIndex = 0;
+            } else
+                colorIndex = readColorIndex;
+        }
+        addPixelToRaster(colorIndex, &pnmBuffer, cols, rows, cmap, cmapSize,
+                         interlace, transparentIndex, alphabits, &pass);
+    }
+}
+
+
+
+static void
+skipExtraneousData(struct decompressor * const decompP) {
+
+    unsigned int byteRead;
+    bool endOfImage;
+    const char * error;
+
+    lzwReadByte(decompP, &byteRead, &endOfImage, &error);
+
+    if (error)
+        strfree(error);
+    else if (!endOfImage) {
+        pm_message("Extraneous data at end of image.  "
+                   "Skipped to end of image");
+
+        while (!endOfImage && !error)
+            lzwReadByte(decompP, &byteRead, &endOfImage, &error);
+
+        if (error) {
+            pm_message("Error encountered skipping to end of image: %s",
+                       error);
+            strfree(error);
+        }
+    }
+}
+
+
+
+static void
 readImageData(FILE *       const ifP, 
               xel **       const xels, 
               unsigned int const cols,
@@ -1035,20 +1271,13 @@ readImageData(FILE *       const ifP,
               unsigned int const cmapSize,
               bool         const interlace,
               int          const transparentIndex,
-              bit **       const alphabits) {
+              bit **       const alphabits,
+              bool         const tolerateBadInput) {
 
     unsigned char lzwMinCodeSize;      
-    enum pass pass;
     struct decompressor decomp;
-    struct pnmBuffer pnmBuffer;
     bool gotMinCodeSize;
 
-    pass = MULT8PLUS0;
-
-    pnmBuffer.xels = xels;
-    pnmBuffer.col  = 0;
-    pnmBuffer.row  = 0;
-
     gotMinCodeSize =  ReadOK(ifP, &lzwMinCodeSize, 1);
     if (!gotMinCodeSize)
         pm_error("GIF stream ends (or read error) "
@@ -1062,29 +1291,10 @@ readImageData(FILE *       const ifP,
 
     lzwInit(&decomp, ifP, lzwMinCodeSize);
 
-    while (pnmBuffer.row < rows) {
-        int const rc = lzwReadByte(&decomp);
+    readRaster(&decomp, xels, cols, rows, cmap, cmapSize, interlace,
+               transparentIndex, alphabits, tolerateBadInput);
 
-        switch (rc) {
-        case -3:
-            pm_error("Error in GIF input stream");
-            break;
-        case -2:
-            pm_error("Error in GIF image: Not enough raster data to fill "
-                     "%u x %u dimensions.  Ran out of raster data in "
-                     "row %u", cols, rows, pnmBuffer.row);
-            break;
-        case -1:
-            pm_error("Premature end of file; no proper GIF closing");
-            break;
-        default:
-            addPixelToRaster(rc, &pnmBuffer, cols, rows, cmap, cmapSize,
-                             interlace, transparentIndex, alphabits, &pass);
-        }
-    }
-    if (lzwReadByte(&decomp) >= 0)
-        pm_message("Extraneous data at end of image.  "
-                   "Skipped to end of image");
+    skipExtraneousData(&decomp);
 
     lzwTerm(&decomp);
 }
@@ -1092,33 +1302,36 @@ readImageData(FILE *       const ifP,
 
 
 static void
-writePnm(FILE *outfile, xel ** const xels, 
-         const int cols, const int rows,
-         const int hasGray, const int hasColor) {
+writePnm(FILE * const outfileP,
+         xel ** const xels, 
+         int    const cols,
+         int    const rows,
+         int    const hasGray,
+         int    const hasColor) {
 /*----------------------------------------------------------------------------
-   Write a PNM image to the current position of file 'outfile' with
+   Write a PNM image to the current position of file *outfileP with
    dimensions 'cols' x 'rows' and raster 'xels'.
    
    Make it PBM, PGM, or PBM according to 'hasGray' and 'hasColor'.
 -----------------------------------------------------------------------------*/
     int format;
-    const char *format_name;
+    const char * formatName;
            
     if (hasColor) {
         format = PPM_FORMAT;
-        format_name = "PPM";
+        formatName = "PPM";
     } else if (hasGray) {
         format = PGM_FORMAT;
-        format_name = "PGM";
+        formatName = "PGM";
     } else {
         format = PBM_FORMAT;
-        format_name = "PBM";
+        formatName = "PBM";
     }
     if (verbose) 
-        pm_message("writing a %s file", format_name);
+        pm_message("writing a %s file", formatName);
     
-    if (outfile) 
-        pnm_writepnm(outfile, xels, cols, rows,
+    if (outfileP) 
+        pnm_writepnm(outfileP, xels, cols, rows,
                      (xelval) GIFMAXVAL, format, FALSE);
 }
 
@@ -1172,7 +1385,7 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
     if (verbose)
         pm_message("GIF format version is '%s'", version);
     
-    if ((!STREQ(version, "87a")) && (!STREQ(version, "89a")))
+    if ((!streq(version, "87a")) && (!streq(version, "89a")))
         pm_error("bad version number, not '87a' or '89a'" );
     
     if (! ReadOK(gifFile,buf,7))
@@ -1223,7 +1436,8 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
 static void
 readExtensions(FILE*          const ifP, 
                struct gif89 * const gif89P,
-               bool *         const eodP) {
+               bool *         const eodP,
+               const char **  const errorP) {
 /*----------------------------------------------------------------------------
    Read extension blocks from the GIF stream to which the file *ifP is
    positioned.  Read up through the image separator that begins the
@@ -1232,33 +1446,50 @@ readExtensions(FILE*          const ifP,
    If we encounter EOD (end of GIF stream) before we find an image 
    separator, we return *eodP == TRUE.  Else *eodP == FALSE.
 
-   If we hit end of file before an EOD marker, we abort the program with
-   an error message.
+   If we hit end of file before an EOD marker, we fail.
 -----------------------------------------------------------------------------*/
     bool imageStart;
     bool eod;
 
+    *errorP = NULL;  /* initial value */
+
     eod = FALSE;
     imageStart = FALSE;
 
     /* Read the image descriptor */
-    while (!imageStart && !eod) {
+    while (!imageStart && !eod && !*errorP) {
         unsigned char c;
+        const char * error;
 
-        if (! ReadOK(ifP,&c,1))
-            pm_error("EOF / read error on image data" );
+        readFile(ifP, &c, 1, &error);
 
-        if (c == ';') {         /* GIF terminator */
-            eod = TRUE;
-        } else if (c == '!') {         /* Extension */
-            if (! ReadOK(ifP,&c,1))
-                pm_error("EOF / "
-                         "read error on extension function code");
-            doExtension(ifP, c, gif89P);
-        } else if (c == ',') 
-            imageStart = TRUE;
-        else 
-            pm_message("bogus character 0x%02x, ignoring", (int) c );
+        if (error) {
+            asprintfN(errorP, "File read error where start of image "
+                      "descriptor or end of GIF expected.  %s",
+                      error);
+            strfree(error);
+        } else {
+            if (c == ';') {         /* GIF terminator */
+                eod = TRUE;
+            } else if (c == '!') {         /* Extension */
+                unsigned char functionCode;
+                const char * error;
+
+                readFile(ifP, &functionCode, 1, &error);
+
+                if (error) {
+                    asprintfN(errorP, "Failed to read function code "
+                              "of GIF extension (immediately after the '!' "
+                              "extension delimiter) from input.  %s", error);
+                    strfree(error);
+                } else {
+                    doExtension(ifP, functionCode, gif89P);
+                }
+            } else if (c == ',') 
+                imageStart = TRUE;
+            else 
+                pm_message("bogus character 0x%02x, ignoring", (int)c);
+        }
     }
     *eodP = eod;
 }
@@ -1272,9 +1503,9 @@ reportImageInfo(unsigned int const cols,
                 unsigned int const localColorMapSize,
                 bool         const interlaced) {
 
-
     pm_message("reading %u by %u%s GIF image",
                cols, rows, interlaced ? " interlaced" : "" );
+
     if (useGlobalColormap)
         pm_message("  Uses global colormap");
     else
@@ -1289,7 +1520,8 @@ convertImage(FILE *           const ifP,
              FILE *           const imageout_file, 
              FILE *           const alphafile, 
              struct gifScreen       gifScreen,
-             struct gif89     const gif89) {
+             struct gif89     const gif89,
+             bool             const tolerateBadInput) {
 /*----------------------------------------------------------------------------
    Read a single GIF image from the current position of file 'ifP'.
 
@@ -1340,7 +1572,8 @@ convertImage(FILE *           const ifP,
                      &hasGray, &hasColor);
         transparencyMessage(gif89.transparent, localColorMap);
         readImageData(ifP, xels, cols, rows, localColorMap, localColorMapSize,
-                      interlaced, gif89.transparent, alphabits);
+                      interlaced, gif89.transparent, alphabits,
+                      tolerateBadInput);
         if (!skipIt) {
             writePnm(imageout_file, xels, cols, rows,
                      hasGray, hasColor);
@@ -1349,7 +1582,8 @@ convertImage(FILE *           const ifP,
         transparencyMessage(gif89.transparent, gifScreen.ColorMap);
         readImageData(ifP, xels, cols, rows, 
                       gifScreen.ColorMap, gifScreen.ColorMapSize,
-                      interlaced, gif89.transparent, alphabits);
+                      interlaced, gif89.transparent, alphabits,
+                      tolerateBadInput);
         if (!skipIt) {
             writePnm(imageout_file, xels, cols, rows,
                      gifScreen.hasGray, gifScreen.hasColor);
@@ -1367,12 +1601,33 @@ convertImage(FILE *           const ifP,
 
 
 static void
+disposeOfReadExtensionsError(const char * const error,
+                             bool         const tolerateBadInput,
+                             unsigned int const imageSeq,
+                             bool *       const eodP) {
+    if (error) {
+        if (tolerateBadInput)
+            pm_message("Error accessing Image %u of stream; no further "
+                       "images can be accessed.  %s",
+                       imageSeq, error);
+        else
+            pm_error("Error accessing Image %u of stream.  %s",
+                     imageSeq, error);
+        strfree(error);
+        *eodP = TRUE;
+    }
+}
+
+
+
+static void
 convertImages(FILE * const ifP, 
               bool   const allImages,
               int    const requestedImageSeq, 
               bool   const drainStream,
               FILE * const imageout_file, 
-              FILE * const alphafile) {
+              FILE * const alphafile,
+              bool   const tolerateBadInput) {
 /*----------------------------------------------------------------------------
    Read a GIF stream from file 'ifP' and write one or more images from
    it as PNM images to file 'imageout_file'.  If the images have transparency
@@ -1407,20 +1662,25 @@ convertImages(FILE * const ifP,
          !eod && (imageSeq <= requestedImageSeq || allImages || drainStream);
          ++imageSeq) {
 
-        readExtensions(ifP, &gif89, &eod);
+        const char * error;
+
+        readExtensions(ifP, &gif89, &eod, &error);
+
+        disposeOfReadExtensionsError(error, tolerateBadInput, imageSeq, &eod);
 
         if (eod) {
             /* GIF stream ends before image with sequence imageSeq */
             if (!allImages && (imageSeq <= requestedImageSeq))
                 pm_error("You requested Image %d, but "
                          "only %d image%s found in GIF stream",
-                         requestedImageSeq+1,
-                         imageSeq, imageSeq>1?"s":"" );
+                         requestedImageSeq + 1,
+                         imageSeq, imageSeq > 1 ? "s" : "");
         } else {
             if (verbose)
                 pm_message("Reading Image Sequence %d", imageSeq);
             convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
-                         imageout_file, alphafile, gifScreen, gif89);
+                         imageout_file, alphafile, gifScreen, gif89,
+                         tolerateBadInput);
         }
     }
 }
@@ -1447,19 +1707,20 @@ main(int argc, char **argv) {
     else
         alpha_file = pm_openw(cmdline.alpha_filename);
 
-    if (alpha_file && STREQ(cmdline.alpha_filename, "-"))
+    if (alpha_file && streq(cmdline.alpha_filename, "-"))
         imageout_file = NULL;
     else
         imageout_file = stdout;
 
     convertImages(ifP, cmdline.all_images, cmdline.image_no, 
-                  !cmdline.quitearly, imageout_file, alpha_file);
+                  !cmdline.quitearly, imageout_file, alpha_file,
+                  cmdline.repair);
 
     pm_close(ifP);
     if (imageout_file != NULL) 
-        pm_close( imageout_file );
+        pm_close(imageout_file);
     if (alpha_file != NULL)
-        pm_close( alpha_file );
+        pm_close(alpha_file);
 
     return 0;
 }
diff --git a/converter/other/hdifftopam.c b/converter/other/hdifftopam.c
index 444d6068..7bfeed9b 100644
--- a/converter/other/hdifftopam.c
+++ b/converter/other/hdifftopam.c
@@ -9,6 +9,7 @@
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -96,7 +97,7 @@ main(int argc, char *argv[]) {
     tuple * outrow;
     tuple * prevrow;
 
-    pnm_init( &argc, argv );
+    pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -106,7 +107,7 @@ main(int argc, char *argv[]) {
 
     if (diffpam.format != PAM_FORMAT) 
         pm_error("Input must be a PAM file, not PNM");
-    else if (!STREQ(diffpam.tuple_type, "hdiff")) 
+    else if (!streq(diffpam.tuple_type, "hdiff")) 
         pm_error("Input tuple type is '%s'.  Must be 'hdiff'",
                  diffpam.tuple_type);
 
diff --git a/converter/other/infotopam.c b/converter/other/infotopam.c
index 4f29eb07..21fa8ee2 100644
--- a/converter/other/infotopam.c
+++ b/converter/other/infotopam.c
@@ -65,6 +65,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/other/jbig/Makefile b/converter/other/jbig/Makefile
index 1b9d7535..b5f4e14a 100644
--- a/converter/other/jbig/Makefile
+++ b/converter/other/jbig/Makefile
@@ -5,13 +5,13 @@ endif
 SUBDIR = converter/other/jbig
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIBJBIG_OBJECTS = jbig.o jbig_tab.o
 
-INCLUDES =
+EXTERN_INCLUDES =
 ifneq ($(JBIGHDR_DIR),NONE)
-  INCLUDES += -I$(JBIGHDR_DIR)
+  EXTERN_INCLUDES += -I$(JBIGHDR_DIR)
 endif
 
 ifneq ($(JBIGHDR_DIR),NONE)
@@ -33,7 +33,7 @@ MERGE_OBJECTS = $(BINARIES:%=%.o2) $(LIBJBIG_OBJECTS)
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 $(BINARIES): %: %.o $(JBIGLIB_DEP) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $< \
diff --git a/converter/other/jpeg2000/Makefile b/converter/other/jpeg2000/Makefile
index bf3f5e4a..f4fee87f 100644
--- a/converter/other/jpeg2000/Makefile
+++ b/converter/other/jpeg2000/Makefile
@@ -7,11 +7,11 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 SUBDIRS = libjasper
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
-INCLUDES =
+EXTERN_INCLUDES =
 ifneq ($(JASPERHDR_DIR),NONE)
-  INCLUDES += -I$(JASPERHDR_DIR)
+  EXTERN_INCLUDES += -I$(JASPERHDR_DIR)
 endif
 
 
@@ -55,7 +55,7 @@ MERGEBINARIES = $(BINARIES)
 .PHONY: all
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB) $(JASPERLIB_USE))
 
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
index e11e9fb4..e6db7658 100644
--- a/converter/other/jpeg2000/jpeg2ktopam.c
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -13,6 +13,7 @@
 #define _XOPEN_SOURCE 600
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/converter/other/jpeg2000/libjasper/Makefile b/converter/other/jpeg2000/libjasper/Makefile
index 965f15ad..ddbd148a 100644
--- a/converter/other/jpeg2000/libjasper/Makefile
+++ b/converter/other/jpeg2000/libjasper/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other/jpeg2000/libjasper
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 SUBDIRS = base jp2 jpc
 LIB_OBJECTS =
@@ -17,7 +17,7 @@ JASPERSRCDIR = $(SRCDIR)/$(SUBDIR)
 
 all: libjasper.a
 
-include $(SRCDIR)/$(SUBDIR)/Makefile.common
+include $(SRCDIR)/$(SUBDIR)/common.mk
 
 # We cheat a bit here -- the real dependencies are all the .o files listed
 # in the part list, but since we don't know what those are, we just do a
diff --git a/converter/other/jpeg2000/libjasper/base/Makefile b/converter/other/jpeg2000/libjasper/base/Makefile
index 0ee65b5e..ad84f043 100644
--- a/converter/other/jpeg2000/libjasper/base/Makefile
+++ b/converter/other/jpeg2000/libjasper/base/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIB_OBJECTS = jas_debug.o jas_getopt.o jas_image.o jas_init.o \
 	      jas_malloc.o jas_seq.o jas_stream.o jas_string.o \
@@ -17,5 +17,5 @@ MERGE_OBJECTS =
 
 all: partlist $(LIB_OBJECTS)
 
-include $(JASPERSRCDIR)/Makefile.common
+include $(JASPERSRCDIR)/common.mk
 
diff --git a/converter/other/jpeg2000/libjasper/base/jas_stream.c b/converter/other/jpeg2000/libjasper/base/jas_stream.c
index f450aabf..4c84e6c2 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_stream.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_stream.c
@@ -130,6 +130,8 @@
 #include <io.h>
 #endif
 
+#include "pm.h"
+
 #include "jasper/jas_types.h"
 #include "jasper/jas_stream.h"
 #include "jasper/jas_malloc.h"
@@ -380,51 +382,6 @@ jas_stream_t *jas_stream_freopen(const char *path, const char *mode, FILE *fp)
 }
 
 
-static int
-tmpfilex(void) {
-/*----------------------------------------------------------------------------
-   This is a copy of pm_tmpfile() without the libnetpbm dependencies
-   and returning a file descriptor instead of stream.
------------------------------------------------------------------------------*/
-    const char libname[] = "jasper";
-
-    int fd;
-    char buf[FILENAME_MAX];
-    const char * tbuf;
-    unsigned int fnamelen;
-
-    fnamelen = strlen(libname) + 10; /* "/" + "_XXXXXX\0" */
-
-    tbuf = getenv("TMPDIR");
-
-    if ((tbuf != NULL) && (strlen(tbuf) > FILENAME_MAX - fnamelen))
-        /* length of TMPDIR value too big for buf */
-        tbuf = NULL;
-
-    buf[FILENAME_MAX - fnamelen -1] = 0;
-
-    if ((tbuf == NULL) || (strlen(tbuf) == 0))
-        /* environment variable not suitable to construct file name.
-           Use default.
-        */
-        strncpy(buf, TMPDIR, sizeof(buf) - fnamelen);
-    else
-        strncpy(buf, tbuf, sizeof(buf) - fnamelen);
-
-    if (buf[strlen(buf) - 1] != '/')
-        strcat (buf, "/");
-    
-    strcat(buf, libname);
-    strcat(buf, "_XXXXXX");
-
-    fd = mkstemp(buf);
-
-    if (fd >= 0)
-        unlink(buf);
-
-    return fd;
-}
-
 jas_stream_t *jas_stream_tmpfile()
 {
 	jas_stream_t *stream;
@@ -444,13 +401,23 @@ jas_stream_t *jas_stream_tmpfile()
 		return 0;
 	}
 	stream->obj_ = obj;
-    
-    /* This is a Netpbm enhancement.  Original Jasper library uses
-       tmpnam(), which is unsafe.
-    */
-    if ((*obj = tmpfilex()) < 0) {
-		jas_stream_destroy(stream);
-		return 0;
+
+    {
+        /* This is a Netpbm enhancement.  Original Jasper library uses
+           tmpnam(), which is unsafe.
+        */
+        jmp_buf jmpbuf;
+        int rc;
+        
+        rc = setjmp(jmpbuf);
+        if (rc == 0) {
+            pm_setjmpbuf(&jmpbuf);
+            *obj = pm_tmpfile_fd();
+        } else {
+            /* pm_tmpfile_fd() threw an error */
+            jas_stream_destroy(stream);
+            return 0;
+        }
     }
 	/* Use full buffering. */
 	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
diff --git a/converter/other/jpeg2000/libjasper/Makefile.common b/converter/other/jpeg2000/libjasper/common.mk
index 525e9c2d..687a9f3f 100644
--- a/converter/other/jpeg2000/libjasper/Makefile.common
+++ b/converter/other/jpeg2000/libjasper/common.mk
@@ -1,4 +1,3 @@
-# -*-makefile-*-    <-- an Emacs control
 # This is common rules for the libjasper subdirectories.
 #
 # Set the following variables before including this:
@@ -19,14 +18,14 @@ $(SUBDIRS:%=%/partlist): %/partlist: $(CURDIR)/%
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
-INCLUDES = -I$(JASPERSRCDIR)/include -Iimportinc
+include $(SRCDIR)/common.mk
 
-include $(SRCDIR)/Makefile.common
+INCLUDES = -I$(JASPERSRCDIR)/include -Iimportinc
 
 DEFS = -DHAVE_LIBM=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_LIMITS_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STDDEF_H=1 -DEXCLUDE_BMP_SUPPORT -DEXCLUDE_RAS_SUPPORT -DEXCLUDE_MIF_SUPPORT -DEXCLUDE_JPG_SUPPORT -DEXCLUDE_PGX_SUPPORT -DEXCLUDE_PNM_SUPPORT
 
 $(LIB_OBJECTS):%.o:%.c
-	$(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(CADD) $<
+	$(CC) -c $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(CADD) $<
 
 $(LIB_OBJECTS): importinc
 
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
new file mode 100644
index 00000000..10c1152d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Primitive Types
+ *
+ * $Id$
+ */
+
+#ifndef JAS_TYPES_H
+#define JAS_TYPES_H
+
+#if defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#endif
+#if defined(HAVE_STDDEF_H)
+#include <stddef.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+#include <sys/types.h>
+#endif
+
+#if defined(HAVE_STDBOOL_H)
+/*
+ * The C language implementation does correctly provide the standard header
+ * file "stdbool.h".
+ */
+#include <stdbool.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The C language implementation does not provide the standard header file
+ * "stdbool.h" as required by ISO/IEC 9899:1999.  Try to compensate for this
+ * braindamage below.
+ */
+#if !defined(bool)
+#define	bool	int
+#endif
+#if !defined(true)
+#define true	1
+#endif
+#if !defined(false)
+#define	false	0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/* pm_config.h defines the FAST integer types if possible (typically by
+   including <inttypes.h>.  If not, the following try to take care
+   of it.
+*/
+/**********/
+#if !defined(INT_FAST8_MAX)
+typedef signed char int_fast8_t;
+#define INT_FAST8_MIN	(-127)
+#define INT_FAST8_MAX	128
+#endif
+/**********/
+#if !defined(UINT_FAST8_MAX)
+typedef unsigned char uint_fast8_t;
+#define UINT_FAST8_MAX	255
+#endif
+/**********/
+#if !defined(INT_FAST16_MAX)
+typedef short int_fast16_t;
+#define INT_FAST16_MIN	SHRT_MIN
+#define INT_FAST16_MAX	SHRT_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST16_MAX)
+typedef unsigned short uint_fast16_t;
+#define UINT_FAST16_MAX	USHRT_MAX
+#endif
+/**********/
+#if !defined(INT_FAST32_MAX)
+typedef int int_fast32_t;
+#define INT_FAST32_MIN	INT_MIN
+#define INT_FAST32_MAX	INT_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST32_MAX)
+typedef unsigned int uint_fast32_t;
+#define UINT_FAST32_MAX	UINT_MAX
+#endif
+/**********/
+#if !defined(INT_FAST64_MAX)
+typedef int int_fast64_t;
+#define INT_FAST64_MIN	LLONG_MIN
+#define INT_FAST64_MAX	LLONG_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST64_MAX)
+typedef unsigned int uint_fast64_t;
+#define UINT_FAST64_MAX	ULLONG_MAX
+#endif
+/**********/
+#endif
+
+/* The below macro is intended to be used for type casts.  By using this
+  macro, type casts can be easily located in the source code with
+  tools like "grep". */
+#define	JAS_CAST(t, e) \
+	((t) (e))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jp2/Makefile b/converter/other/jpeg2000/libjasper/jp2/Makefile
index 254b7f56..65838cc2 100644
--- a/converter/other/jpeg2000/libjasper/jp2/Makefile
+++ b/converter/other/jpeg2000/libjasper/jp2/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIB_OBJECTS = jp2_cod.o jp2_dec.o jp2_enc.o
 
@@ -15,5 +15,5 @@ MERGE_OBJECTS =
 
 all: partlist $(LIB_OBJECTS)
 
-include $(JASPERSRCDIR)/Makefile.common
+include $(JASPERSRCDIR)/common.mk
 
diff --git a/converter/other/jpeg2000/libjasper/jpc/Makefile b/converter/other/jpeg2000/libjasper/jpc/Makefile
index e176bd48..ffc4c64f 100644
--- a/converter/other/jpeg2000/libjasper/jpc/Makefile
+++ b/converter/other/jpeg2000/libjasper/jpc/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIB_OBJECTS = jpc_bs.o jpc_cs.o jpc_dec.o jpc_enc.o \
 	jpc_math.o jpc_mct.o jpc_mqcod.o jpc_mqdec.o jpc_mqenc.o \
@@ -18,5 +18,5 @@ MERGE_OBJECTS =
 
 all: partlist $(LIB_OBJECTS)
 
-include $(JASPERSRCDIR)/Makefile.common
+include $(JASPERSRCDIR)/common.mk
 
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
index d860847d..72e3ac37 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
@@ -1,3 +1,72 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "jpc_math.h"
+
+
+
+/* Calculate the integer quantity floor(log2(x)), where x is a positive
+  integer. */
+int
+jpc_floorlog2(int const arg) {
+
+	int y;
+    int x;
+
+	assert(arg > 0);
+
+	y = 0;
+    x = arg;
+	while (x > 1) {
+		x >>= 1;
+		++y;
+	}
+	return y;
+}
+
+
+
+/*
+  jpc_floorlog2() and jpc_firstone() do the same thing.
+  The only difference is how input 0 is handled.
+
+n                  : 0 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 
+ceil(log2(n))      : x 0  1  2  2  3  3  3  3  4  4  4  4  4  4  4  4  5  5  5 
+floor(log2(n))     : x 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+31-__builtin_clz(n): x 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+jpc_floorlog2(n)   : x 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+jpc_firstone(n)    :-1 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+
+*/
+
+
+
+int
+jpc_firstone(int const arg) {
+/*---------------------------------------------------------------------------- 
+  Calculate the bit position of the first leading one in a nonnegative
+  integer.
+-----------------------------------------------------------------------------*/
+	int n;
+    int x;
+
+	assert(arg >= 0);
+
+	n = -1;
+    x = arg;
+	while (x > 0) {
+		x >>= 1;
+		++n;
+	}
+	return n;
+}
+
+
+
 /*
  * Copyright (c) 1999-2000 Image Power, Inc. and the University of
  *   British Columbia.
@@ -109,62 +178,3 @@
  * 
  * __END_OF_JASPER_LICENSE__
  */
-
-/*
- * Math Library
- *
- * $Id$
- */
-
-/******************************************************************************\
-* Includes
-\******************************************************************************/
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include "jpc_math.h"
-
-/******************************************************************************\
-* Miscellaneous Functions
-\******************************************************************************/
-
-/* Calculate the integer quantity floor(log2(x)), where x is a positive
-  integer. */
-int jpc_floorlog2(int x)
-{
-	int y;
-
-	/* The argument must be positive. */
-	assert(x > 0);
-
-	y = 0;
-	while (x > 1) {
-		x >>= 1;
-		++y;
-	}
-	return y;
-}
-
-/* Calculate the bit position of the first leading one in a nonnegative
-  integer. */
-/* This function is the basically the same as ceillog2(x), except that the
-  allowable range for x is slightly different. */
-int jpc_firstone(int x)
-{
-	int n;
-
-	/* The argument must be nonnegative. */
-	assert(x >= 0);
-
-	n = -1;
-	while (x > 0) {
-		x >>= 1;
-		++n;
-	}
-	return n;
-}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
index 1ed0dd90..1d41d5c5 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
@@ -205,16 +205,6 @@ static void jpc_qmfb1d_split(jpc_fix_t *startptr, int startind, int endind,
 	llen = lendind - lstartind;
 	hlen = hendind - hstartind;
 
-#if defined(WIN32)
-	/* Get a buffer. */
-	if (bufsize > QMFB_SPLITBUFSIZE) {
-		if (!(buf = jas_malloc(bufsize * sizeof(jpc_fix_t)))) {
-			/* We have no choice but to commit suicide in this case. */
-			abort();
-		}
-	}
-#endif
-
 	if (hstartind < lstartind) {
 		/* The first sample in the input signal is to appear
 		  in the highpass subband signal. */
@@ -294,13 +284,6 @@ static void jpc_qmfb1d_split(jpc_fix_t *startptr, int startind, int endind,
 			hptr -= step;
 		}
 	}
-
-#if defined(WIN32)
-	/* If the split buffer was allocated on the heap, free this memory. */
-	if (buf != splitbuf) {
-		jas_free(buf);
-	}
-#endif
 }
 
 static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
@@ -320,16 +303,6 @@ static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
 	register int n;
 	int state;
 
-#if defined(WIN32)
-	/* Allocate memory for the join buffer from the heap. */
-	if (bufsize > QMFB_JOINBUFSIZE) {
-		if (!(buf = jas_malloc(bufsize * sizeof(jpc_fix_t)))) {
-			/* We have no choice but to commit suicide. */
-			abort();
-		}
-	}
-#endif
-
 	twostep = step << 1;
 	llen = lendind - lstartind;
 	hlen = hendind - hstartind;
@@ -414,13 +387,6 @@ static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
 			state ^= 1;
 		}
 	}
-
-#if defined(WIN32)
-	/* If the join buffer was allocated on the heap, free this memory. */
-	if (buf != joinbuf) {
-		jas_free(buf);
-	}
-#endif
 }
 
 /******************************************************************************\
diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
index 851d2bf9..70774725 100644
--- a/converter/other/jpeg2000/pamtojpeg2k.c
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -13,6 +13,7 @@
 #define _XOPEN_SOURCE 600
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/converter/other/jpegdatasource.c b/converter/other/jpegdatasource.c
index 5c1070e4..1f53c2a4 100644
--- a/converter/other/jpegdatasource.c
+++ b/converter/other/jpegdatasource.c
@@ -45,6 +45,11 @@ struct sourceManager {
     */
     struct jpeg_source_mgr jpegSourceMgr;
     FILE * ifP;
+    bool prematureEof;
+        /* We have been asked for data and were unable to comply because
+           the file had no more to give (so we supplied EOI markers
+           instead).
+        */
     JOCTET * currentBuffer;
     JOCTET * nextBuffer;
     unsigned int bytesInNextBuffer;
@@ -66,6 +71,10 @@ dsInitSource(j_decompress_ptr const cinfoP) {
 
 
 
+static const JOCTET jfifEoiMarker[] = {0xff, JPEG_EOI};
+    /* An EOI (end of image) marker */
+
+
 static boolean
 dsFillInputBuffer(j_decompress_ptr const cinfoP) {
 /*----------------------------------------------------------------------------
@@ -74,9 +83,19 @@ dsFillInputBuffer(j_decompress_ptr const cinfoP) {
 -----------------------------------------------------------------------------*/
     struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src;
 
-    if (srcP->bytesInNextBuffer == 0) 
-        pm_error("End-of-file encountered in the middle of JPEG image.");
-    else {
+    if (srcP->bytesInNextBuffer == 0) {
+        /* The decompressor expects more bytes, but there aren't any, so
+           the file is corrupted -- probably truncated.  We want the
+           decompressor to decompress whatever it's read so far, so we
+           synthesize an EOI marker here, but we also set error state
+           in the source manager.  The decompressor will recognize the
+           truncation and pad out the image with gray.
+        */
+        srcP->prematureEof = TRUE;
+        
+        srcP->jpegSourceMgr.next_input_byte = jfifEoiMarker;
+        srcP->jpegSourceMgr.bytes_in_buffer = sizeof(jfifEoiMarker);
+    } else {
         /* Rotate the buffers */
         srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer;
         srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer;
@@ -87,7 +106,7 @@ dsFillInputBuffer(j_decompress_ptr const cinfoP) {
             srcP->currentBuffer = tmp;
         }
 
-        /* Fill the new 'next' buffer */
+        /* Fill the new "next" buffer */
         srcP->bytesInNextBuffer = 
             fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
     }
@@ -139,6 +158,14 @@ dsDataLeft(struct sourceManager * const srcP) {
 
 
 
+bool
+dsPrematureEof(struct sourceManager * const srcP) {
+
+    return srcP->prematureEof;
+}
+
+
+
 struct sourceManager * 
 dsCreateSource(const char * const fileName) {
 
@@ -156,6 +183,7 @@ dsCreateSource(const char * const fileName) {
     srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart;
     srcP->jpegSourceMgr.term_source = dsTermSource;
     
+    srcP->prematureEof = FALSE;
     srcP->currentBuffer = srcP->buffer1;
     srcP->nextBuffer = srcP->buffer2;
     srcP->jpegSourceMgr.bytes_in_buffer = 
diff --git a/converter/other/jpegdatasource.h b/converter/other/jpegdatasource.h
index 07f17389..58648fe4 100644
--- a/converter/other/jpegdatasource.h
+++ b/converter/other/jpegdatasource.h
@@ -12,6 +12,9 @@ dsDestroySource(struct sourceManager * const srcP);
 bool
 dsDataLeft(struct sourceManager * const srcP);
 
+bool
+dsPrematureEof(struct sourceManager * const srcP);
+
 struct jpeg_source_mgr *
 dsJpegSourceMgr(struct sourceManager * const srcP);
 
diff --git a/converter/other/jpegtopnm.c b/converter/other/jpegtopnm.c
index 60ae7e42..07a7dfb0 100644
--- a/converter/other/jpegtopnm.c
+++ b/converter/other/jpegtopnm.c
@@ -61,6 +61,8 @@
    itself, but doesn't.
 */
 #include <jpeglib.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -109,6 +111,7 @@ struct cmdlineInfo {
     unsigned int comments;
     unsigned int dumpexif;
     unsigned int multiple;
+    unsigned int repair;
 };
 
 
@@ -116,9 +119,9 @@ static bool displayComments;
     /* User wants comments from the JPEG to be displayed */
 
 static void 
-interpret_maxmemory (bool         const maxmemorySpec,
-                     const char * const maxmemory, 
-                     long int *   const max_memory_to_use_p) { 
+interpret_maxmemory(bool         const maxmemorySpec,
+                    const char * const maxmemory, 
+                    long int *   const max_memory_to_use_p) { 
 /*----------------------------------------------------------------------------
    Interpret the "maxmemory" command line option.
 -----------------------------------------------------------------------------*/
@@ -157,8 +160,9 @@ interpret_adobe(const int adobe, const int notadobe,
 
 
 static void
-parse_command_line(const int argc, char ** argv,
-                   struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int                  const argc,
+                 char **              const argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -173,10 +177,10 @@ parse_command_line(const int argc, char ** argv,
          */
     optStruct3 opt;
 
-    int i;  /* local loop variable */
+    unsigned int i;  /* local loop variable */
 
-    char *maxmemory;
-    char *dctval;
+    const char * maxmemory;
+    const char * dctval;
     unsigned int adobe, notadobe;
 
     unsigned int tracelevelSpec, exifSpec, dctvalSpec, maxmemorySpec;
@@ -206,6 +210,7 @@ parse_command_line(const int argc, char ** argv,
             &exifSpec, 0);
     OPTENT3(0, "dumpexif",    OPT_FLAG,   NULL, &cmdlineP->dumpexif,      0);
     OPTENT3(0, "multiple",    OPT_FLAG,   NULL, &cmdlineP->multiple,      0);
+    OPTENT3(0, "repair",      OPT_FLAG,   NULL, &cmdlineP->repair,        0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -213,7 +218,8 @@ parse_command_line(const int argc, char ** argv,
 
     /* Make private copy of arguments for optParseOptions to corrupt */
     argc_parse = argc;
-    for (i=0; i < argc; i++) argv_parse[i] = argv[i];
+    for (i=0; i < argc; ++i)
+        argv_parse[i] = argv[i];
 
     optParseOptions3( &argc_parse, argv_parse, opt, sizeof(opt), 0);
         /* Uses and sets argc_parse, argv_parse, 
@@ -236,11 +242,11 @@ parse_command_line(const int argc, char ** argv,
     if (!dctvalSpec)
         cmdlineP->dct_method = JDCT_DEFAULT;
     else {
-        if (STREQ(dctval, "int"))
+        if (streq(dctval, "int"))
             cmdlineP->dct_method = JDCT_ISLOW;
-        else if (STREQ(dctval, "fast"))
+        else if (streq(dctval, "fast"))
             cmdlineP->dct_method = JDCT_IFAST;
-        else if (STREQ(dctval, "float"))
+        else if (streq(dctval, "float"))
             cmdlineP->dct_method = JDCT_FLOAT;
         else pm_error("Invalid value for the --dct option: '%s'.", dctval);
     }
@@ -458,32 +464,34 @@ read_rgb(JSAMPLE *ptr, const enum colorspace color_space,
    copy_pixel_row().  But it would be impractical to allocate and free
    the storage with every call to copy_pixel_row().
 */
-static xel *pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
+static xel * pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
 
 static void
-copy_pixel_row(const JSAMPROW jpegbuffer, const int width, 
-               const unsigned int samples_per_pixel, 
-               const enum colorspace color_space,
-               const unsigned int maxval,
-               FILE * const output_file, const int output_type) {
-  JSAMPLE *ptr;
-  unsigned int output_cursor;     /* Cursor into output buffer 'pnmbuffer' */
-
-  ptr = jpegbuffer;  /* Start at beginning of input row */
-
-  for (output_cursor = 0; output_cursor < width; output_cursor++) {
-      xel current_pixel;
-      if (samples_per_pixel >= 3) {
-          const rgb_type * const rgb_p = read_rgb(ptr, color_space, maxval);
-          PPM_ASSIGN(current_pixel, rgb_p->r, rgb_p->g, rgb_p->b);
-      } else {
-          PNM_ASSIGN1(current_pixel, GETJSAMPLE(*ptr));
-      }
-      ptr += samples_per_pixel;  /* move to next pixel of input */
-      pnmbuffer[output_cursor] = current_pixel;
-  }
-  pnm_writepnmrow(output_file, pnmbuffer, width,
-                  maxval, output_type, FALSE);
+copyPixelRow(JSAMPROW        const jpegbuffer,
+             unsigned int    const width, 
+             unsigned int    const samplesPerPixel, 
+             enum colorspace const colorSpace,
+             FILE *          const ofP,
+             int             const format,
+             xelval          const maxval) {
+
+    JSAMPLE * ptr;
+    unsigned int outputCursor;     /* Cursor into output buffer 'pnmbuffer' */
+
+    ptr = &jpegbuffer[0];  /* Start at beginning of input row */
+    
+    for (outputCursor = 0; outputCursor < width; ++outputCursor) {
+        xel currentPixel;
+        if (samplesPerPixel >= 3) {
+            const rgb_type * const rgb_p = read_rgb(ptr, colorSpace, maxval);
+            PPM_ASSIGN(currentPixel, rgb_p->r, rgb_p->g, rgb_p->b);
+        } else {
+            PNM_ASSIGN1(currentPixel, GETJSAMPLE(*ptr));
+        }
+        ptr += samplesPerPixel;  /* move to next pixel of input */
+        pnmbuffer[outputCursor] = currentPixel;
+    }
+    pnm_writepnmrow(ofP, pnmbuffer, width, maxval, format, FALSE);
 }
 
 
@@ -796,17 +804,42 @@ computeColorSpace(struct jpeg_decompress_struct * const cinfoP,
 
 
 static void
+convertRaster(struct jpeg_decompress_struct * const cinfoP,
+              enum colorspace                 const color_space,
+              FILE *                          const ofP,
+              xelval                          const format,
+              unsigned int                    const maxval) {
+              
+    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
+
+    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
+                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
+                   cinfoP->output_width * cinfoP->output_components, 
+                   (JDIMENSION) 1)
+        )[0];
+
+    while (cinfoP->output_scanline < cinfoP->output_height) {
+        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
+        if (ofP)
+            copyPixelRow(jpegbuffer, cinfoP->output_width, 
+                         cinfoP->out_color_components,
+                         color_space, ofP, format, maxval);
+    }
+}
+
+
+
+static void
 convertImage(FILE *                          const ofP, 
              struct cmdlineInfo              const cmdline,
              struct jpeg_decompress_struct * const cinfoP) {
 
-    int output_type;
+    int format;
         /* The type of output file, PGM or PPM.  Value is either PPM_TYPE
            or PGM_TYPE, which conveniently also pass as format values
            PPM_FORMAT and PGM_FORMAT.
         */
-    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
-    unsigned int maxval;  
+    xelval maxval;  
         /* The maximum value of a sample (color component), both in the input
            and the output.
         */
@@ -817,43 +850,30 @@ convertImage(FILE *                          const ofP,
                    cmdline.dct_method, 
                    cmdline.max_memory_to_use, cmdline.nosmooth);
                    
-    set_color_spaces(cinfoP->jpeg_color_space, &output_type, 
+    set_color_spaces(cinfoP->jpeg_color_space, &format,
                      &cinfoP->out_color_space);
 
-    maxval = (1 << cinfoP->data_precision) - 1;
+    maxval = pm_bitstomaxval(cinfoP->data_precision);
 
     if (cmdline.verbose) 
-        tellDetails(*cinfoP, maxval, output_type);
+        tellDetails(*cinfoP, maxval, format);
 
     /* Calculate output image dimensions so we can allocate space */
     jpeg_calc_output_dimensions(cinfoP);
 
-    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
-                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
-                   cinfoP->output_width * cinfoP->output_components, 
-                   (JDIMENSION) 1)
-        )[0];
-
     /* Start decompressor */
     jpeg_start_decompress(cinfoP);
 
     if (ofP)
         /* Write pnm output header */
         pnm_writepnminit(ofP, cinfoP->output_width, cinfoP->output_height,
-                         maxval, output_type, FALSE);
+                         maxval, format, FALSE);
 
     pnmbuffer = pnm_allocrow(cinfoP->output_width);
     
     color_space = computeColorSpace(cinfoP, cmdline.inklevel);
-
-    /* Process data */
-    while (cinfoP->output_scanline < cinfoP->output_height) {
-        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
-        if (ofP)
-            copy_pixel_row(jpegbuffer, cinfoP->output_width, 
-                           cinfoP->out_color_components,
-                           color_space, maxval, ofP, output_type);
-    }
+    
+    convertRaster(cinfoP, color_space, ofP, format, maxval);
 
     if (cmdline.comments)
         print_comments(*cinfoP);
@@ -905,17 +925,25 @@ convertImages(FILE *                          const ofP,
             convertImage(ofP, cmdline, cinfoP);
         }
     } else {
-        if (dsDataLeft(sourceManagerP))
+        if (dsDataLeft(sourceManagerP)) {
             convertImage(ofP, cmdline, cinfoP);
-        else
+        } else
             pm_error("Input stream is empty");
     }
+    if (dsPrematureEof(sourceManagerP)) {
+        if (cmdline.repair)
+            pm_message("Premature EOF on input; repaired by padding end "
+                       "of image.");
+        else
+            pm_error("Premature EOF on input.  Use -repair to salvage.");
+    }
 }
 
 
 
 int
 main(int argc, char **argv) {
+
     FILE * ofP;
     struct cmdlineInfo cmdline;
     struct jpeg_decompress_struct cinfo;
@@ -924,9 +952,9 @@ main(int argc, char **argv) {
 
     pnm_init(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
-    if (cmdline.exif_filespec && STREQ(cmdline.exif_filespec, "-"))
+    if (cmdline.exif_filespec && streq(cmdline.exif_filespec, "-"))
         /* He's got exif going to stdout, so there can be no image output */
         ofP = NULL;
     else
@@ -967,4 +995,3 @@ main(int argc, char **argv) {
   
     exit(jerr.num_warnings > 0 ? EXIT_WARNING : EXIT_SUCCESS);
 }
-
diff --git a/converter/other/pamtodjvurle.c b/converter/other/pamtodjvurle.c
index ae35e81d..2d26eeb0 100644
--- a/converter/other/pamtodjvurle.c
+++ b/converter/other/pamtodjvurle.c
@@ -16,6 +16,7 @@
 #include <stdio.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pammap.h"
 #include "shhopt.h"
@@ -179,7 +180,7 @@ writeRleRun(FILE *       const ofP,
   
   'transcolor' is a 3-deep tuple with the same maxval as the image.
 -----------------------------------------------------------------------------*/
-    uint32n rlevalue;         /* RLE-encoded color/valuex */
+    uint32_t rlevalue;         /* RLE-encoded color/valuex */
     int index;
 
     if (count > 0) {
diff --git a/converter/other/pamtofits.c b/converter/other/pamtofits.c
index ec271ff3..7a1c70de 100644
--- a/converter/other/pamtofits.c
+++ b/converter/other/pamtofits.c
@@ -20,10 +20,17 @@
 ** in the FITS header, but do not cause the data to be rescaled.
 */
 
+/*
+  The official specification of FITS format (which is for more than
+  just visual images) is at
+  ftp://legacy.gsfc.nasa.gov/fits_info/fits_office/fits_standard.pdf
+*/
+
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -39,10 +46,9 @@ struct cmdlineInfo {
 
 
 static void 
-parseCommandLine(int argc, 
-                 char ** argv, 
-                 struct cmdlineInfo  * const cmdlineP) {
-/* --------------------------------------------------------------------------
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*--------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
 
@@ -52,8 +58,8 @@ parseCommandLine(int argc,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
-    optEntry *option_def;
-    /* Instructions to optParseOptions3 on how to parse our options. */
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int minSpec;
@@ -98,7 +104,6 @@ parseCommandLine(int argc,
 
 
 
-
 static void
 writeHeaderCard(const char * const s) {
 /*----------------------------------------------------------------------------
@@ -203,6 +208,14 @@ writeRaster(struct pam * const pamP,
             unsigned int const bitpix,
             int          const offset) {
 
+    /* Note: the FITS specification does not give the association between
+       file position and image position (i.e. is the first pixel in the
+       file the top left, bottom left, etc.).  We use the common sense,
+       popular order of row major, top to bottom, left to right.  This
+       has been the case and accepted since 1989, but in 2008, we discovered
+       that Gimp and ImageMagick do bottom to top.
+    */
+
     unsigned int plane;
 
     for (plane = 0; plane < pamP->depth; ++plane) {
diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c
new file mode 100644
index 00000000..0c8c0f9e
--- /dev/null
+++ b/converter/other/pamtogif.c
@@ -0,0 +1,1977 @@
+/*=============================================================================
+                              pamtogif
+===============================================================================
+  Convert a Netpbm image to GIF
+
+  History and copyright information is at the end of the file.
+=============================================================================*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+#define MAXCMAPSIZE 256
+
+static unsigned int const gifMaxval = 255;
+
+static bool verbose;
+
+
+typedef int stringCode;
+    /* A code to be place in the GIF raster.  It represents
+       a string of one or more pixels.  You interpret this in the context
+       of a current code size.  The lower half of the values representable
+       in the current code size represent singleton strings and the value
+       is simply the value of the one pixel in the string.  The first two
+       values in the upper half of the range are the clear code and EOF
+       code, respectively.  The rest of the values represent multi-pixel
+       strings.  The mapping between value and the sequence of pixels
+       changes throughout the image.
+
+       A variable of this type sometimes has the value -1 instead of
+       a string code due to cheesy programming.
+
+       Ergo, this data structure must be signed and at least BITS bits
+       wide plus sign bit.
+    */
+
+
+struct cmap {
+    /* This is the information for the GIF colormap (aka palette). */
+
+    struct pam pam;
+        /* Gives depth and maxval for colors in color[] */
+    tuple color[MAXCMAPSIZE];
+        /* Maps a color index, as is found in the raster part of the
+           GIF, to color.
+        */
+    unsigned int cmapSize;
+        /* Number of entries in the GIF colormap.  I.e. number of colors
+           in the image, plus possibly one fake transparency color.
+        */
+    bool haveTransparent;
+        /* The colormap contains an entry for transparent pixels */
+    unsigned int transparent;
+        /* color index number in GIF palette of the color that is to
+           be transparent.
+
+           Meaningful only if 'haveTransparent' is true.
+        */
+    tuplehash tuplehash;
+        /* A hash table to translate color to GIF colormap index. */
+};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filespec; /* Filespec of input file */
+    const char *alphacolor;     /* -alphacolor option value or default */
+    unsigned int interlace;     /* -interlace option value */
+    unsigned int sort;          /* -sort option value */
+    const char *mapfile;        /* -mapfile option value.  NULL if none. */
+    const char *transparent;    /* -transparent option value.  NULL if none. */
+    const char *comment;        /* -comment option value; NULL if none */
+    unsigned int nolzw;         /* -nolzw option */
+    float aspect;               /* -aspect option value (the ratio).  */
+    unsigned int verbose;
+};
+
+
+
+
+
+static unsigned int
+pamAlphaPlane(struct pam * const pamP) {
+
+    unsigned int alphaPlane;
+
+    if (streq(pamP->tuple_type, "RGB_ALPHA"))
+        alphaPlane = 3;
+    else if (streq(pamP->tuple_type, "GRAYSCALE_ALPHA"))
+        alphaPlane = 2;
+    else if (streq(pamP->tuple_type, "BLACKANDWHITE_ALPHA"))
+        alphaPlane = 2;
+    else
+        alphaPlane = 0;
+    
+    if (alphaPlane >= pamP->depth)
+        pm_error("Tuple type is '%s', but depth (%u) is less than %u",
+                 pamP->tuple_type, pamP->depth, alphaPlane + 1);
+
+    return alphaPlane;
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Parse the program arguments (given by argc and argv) into a form
+   the program can deal with more easily -- a cmdline_info structure.
+   If the syntax is invalid, issue a message and exit the program via
+   pm_error().
+
+   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'ed */
+    optStruct3 opt;  /* set by OPTENT3 */
+    unsigned int option_def_index;
+
+    unsigned int aspectSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "interlace",   OPT_FLAG,   
+            NULL,                       &cmdlineP->interlace, 0);
+    OPTENT3(0,   "sort",        OPT_FLAG,   
+            NULL,                       &cmdlineP->sort, 0);
+    OPTENT3(0,   "nolzw",       OPT_FLAG,   
+            NULL,                       &cmdlineP->nolzw, 0);
+    OPTENT3(0,   "mapfile",     OPT_STRING, 
+            &cmdlineP->mapfile,        NULL, 0);
+    OPTENT3(0,   "transparent", OPT_STRING, 
+            &cmdlineP->transparent,    NULL, 0);
+    OPTENT3(0,   "comment",     OPT_STRING, 
+            &cmdlineP->comment,        NULL, 0);
+    OPTENT3(0,   "alphacolor",  OPT_STRING, 
+            &cmdlineP->alphacolor,     NULL, 0);
+    OPTENT3(0,   "aspect",      OPT_FLOAT, 
+            &cmdlineP->aspect,         &aspectSpec, 0);
+    OPTENT3(0,   "verbose",     OPT_FLAG, 
+            NULL,                      &cmdlineP->verbose, 0);
+    
+    /* Set the defaults */
+    cmdlineP->mapfile = NULL;
+    cmdlineP->transparent = NULL;  /* no transparency */
+    cmdlineP->comment = NULL;      /* no comment */
+    cmdlineP->alphacolor = "rgb:0/0/0";      
+        /* We could say "black" here, but then we depend on the color names
+           database existing.
+        */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->input_filespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->input_filespec = argv[1];
+        
+    if (aspectSpec) { 
+        if (cmdlineP->aspect < 0.25  || cmdlineP->aspect > 4.21875)
+            pm_error("Invalid -aspect value: %f.  "
+                     "GIF allows only the range 0.25-4.0 .",
+                     cmdlineP->aspect);
+        else if (cmdlineP->aspect > 4.0)
+            pm_message("Warning: "
+                       "You specified an aspect ratio over 4.0: %f.  "
+                       "This will result in an invalid GIF.",
+                       cmdlineP->aspect);
+    } else
+        cmdlineP->aspect = 1.0;
+}
+
+
+
+/*
+ * Write out a word to the GIF file
+ */
+static void
+Putword(int const w, FILE * const fp) {
+
+    fputc( w & 0xff, fp );
+    fputc( (w / 256) & 0xff, fp );
+}
+
+
+
+static int
+closestColor(tuple         const color,
+             struct pam *  const pamP,
+             struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Return the colormap index of the color in the colormap *cmapP
+   that is closest to the color 'color', whose format is specified by 
+   *pamP.
+
+   Also add 'color' to the colormap hash, with the colormap index we
+   are returning.  Caller must ensure that the color is not already in
+   there.
+-----------------------------------------------------------------------------*/
+    unsigned int const nComp = pamP->depth >= 3 ? 3 : 1;
+        /* Number of color components (not alpha) in 'color' */
+    
+    unsigned int i;
+    unsigned int imin, dmin;
+    bool fits;
+
+    dmin = UINT_MAX;
+    imin = 0;
+    for (i = 0; i < cmapP->cmapSize; ++i) {
+        unsigned int distance;
+        unsigned int plane;
+
+        for (distance = 0, plane = 0; plane < nComp; ++plane)
+            /* Divide by 4 is to avoid arithmetic overflow */
+            distance += SQR(color[plane] - cmapP->color[i][plane]) / 4;
+
+        if (distance < dmin) {
+            dmin = distance;
+            imin = i; 
+        } 
+    }
+    pnm_addtotuplehash(pamP, cmapP->tuplehash, color, imin, &fits);
+
+    return imin;
+}
+
+
+
+enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1};
+
+
+typedef struct {
+    struct pam pam;
+        /* Description of input file/image.  The position of the file
+           is also part of the state of this rowReader.
+        */
+    pm_filepos rasterPos;
+        /* Position in file fileP of the start of the raster */
+    bool interlace;
+        /* We're accessing the image in interlace fashion */
+    bool eof;
+        /* The image is at EOF (we have returned all of the rows) */
+    unsigned int nextRow;
+        /* Number of row to which input file is positioned;
+           meaningless if 'eof'.
+        */
+    enum pass pass;
+        /* The interlace pass.  Undefined if !interlace */
+    tuple * discardBuffer;
+        /* A bitbucket for rows we read in order to advance the file
+           position.
+        */
+} rowReader;
+
+
+
+static rowReader *
+rowReader_create(struct pam * const pamP,
+                 pm_filepos   const rasterPos,
+                 bool         const interlace) {
+
+    rowReader * rdrP;
+
+    MALLOCVAR_NOFAIL(rdrP);
+
+    rdrP->pam         = *pamP;
+    rdrP->rasterPos   = rasterPos;
+    rdrP->interlace   = interlace;
+    rdrP->eof         = FALSE;
+    rdrP->pass        = MULT8PLUS0;
+
+    pm_seek2(rdrP->pam.file, &rasterPos, sizeof(rasterPos));
+    rdrP->nextRow = 0;
+
+    rdrP->discardBuffer = pnm_allocpamrow(&rdrP->pam);
+
+    return rdrP;
+}
+
+
+
+static void
+rowReader_destroy(rowReader * const rdrP) {
+
+    pnm_freepamrow(rdrP->discardBuffer);
+    free(rdrP);
+}
+
+
+
+static void
+rowReaderSkipRows(rowReader *  const rdrP,
+                  unsigned int const rowCount,
+                  bool *       const eofP) {
+/*----------------------------------------------------------------------------
+   Skip over the next 'rowCount' rows of the input file.
+
+   Iff there aren't at least 'rowCount' rows left, return *eofP == TRUE.
+-----------------------------------------------------------------------------*/
+    if (rdrP->nextRow + rowCount >= rdrP->pam.height)
+        *eofP = TRUE;
+    else {
+        /* This could be made faster if need be by adding a libnetpbm
+           row skip function.  Except with the plain formats, that could
+           just compute the next row position and fseek() to it.
+           pnm_readpamrow() with NULL for the output pointer would be a
+           good interface for a row skip function.
+        */
+        unsigned int i;
+
+        *eofP = FALSE;
+
+        for (i = 0; i < rowCount; ++i)
+            pnm_readpamrow(&rdrP->pam, rdrP->discardBuffer);
+
+        rdrP->nextRow += rowCount;
+    }
+}
+
+
+
+static void
+rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
+/*----------------------------------------------------------------------------
+  Position reader to the next row in the interlace pattern, assuming it
+  is now positioned immediately after the current row.
+-----------------------------------------------------------------------------*/
+    bool endOfPass;
+
+    /* There are 4 passes:
+       MULT8PLUS0: Rows 0, 8, 16, 24, 32, etc.
+       MULT8PLUS4: Rows 4, 12, 20, 28, etc.
+       MULT4PLUS2: Rows 2, 6, 10, 14, etc.
+       MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
+    */
+    
+    switch (rdrP->pass) {
+    case MULT8PLUS0:
+        rowReaderSkipRows(rdrP, 7, &endOfPass);
+        break;
+    case MULT8PLUS4:
+        rowReaderSkipRows(rdrP, 7, &endOfPass);
+        break;
+    case MULT4PLUS2:
+        rowReaderSkipRows(rdrP, 3, &endOfPass);
+        break;
+    case MULT2PLUS1:
+        rowReaderSkipRows(rdrP, 1, &endOfPass);
+        break;
+    }
+
+    /* Note that if there are more than 4 rows, the sequence of passes
+       is sequential, but when there are fewer than 4, reading may skip
+       e.g. from MULT8PLUS0 to MULT4PLUS2.
+    */
+    while (endOfPass && !rdrP->eof) {
+        pm_seek2(rdrP->pam.file, &rdrP->rasterPos, sizeof(rdrP->rasterPos));
+        rdrP->nextRow = 0;
+
+        switch (rdrP->pass) {
+        case MULT8PLUS0:
+            rdrP->pass = MULT8PLUS4;
+            rowReaderSkipRows(rdrP, 4, &endOfPass);
+            break;
+        case MULT8PLUS4:
+            rdrP->pass = MULT4PLUS2;
+            rowReaderSkipRows(rdrP, 2, &endOfPass);
+            break;
+        case MULT4PLUS2:
+            rdrP->pass = MULT2PLUS1;
+            rowReaderSkipRows(rdrP, 1, &endOfPass);
+            break;
+        case MULT2PLUS1:
+            rdrP->eof = TRUE;
+            break;
+        }
+    }
+}
+
+
+
+
+static void
+rowReaderGotoNextStraightRow(rowReader * const rdrP) {
+/*----------------------------------------------------------------------------
+  Position reader to the next row in a straight, non-interlace
+  pattern, assuming the file is now positioned immediately after the
+  current row.
+
+  This is trivial, since the next row _is_ immediately after the current
+  row, except in the case that there are no more rows.
+-----------------------------------------------------------------------------*/
+    if (rdrP->nextRow >= rdrP->pam.height)
+        rdrP->eof = TRUE;
+}
+
+
+
+static void
+rowReader_read(rowReader * const rdrP,
+               tuple *     const tuplerow) {
+
+    if (rdrP->eof)
+        pm_error("INTERNAL ERROR: rowReader attempted to read beyond end "
+                 "of image");
+
+    pnm_readpamrow(&rdrP->pam, tuplerow);
+    ++rdrP->nextRow;
+    
+    if (rdrP->interlace)
+        rowReaderGotoNextInterlaceRow(rdrP);
+    else
+        rowReaderGotoNextStraightRow(rdrP);
+}
+
+
+
+static unsigned int
+gifPixel(struct pam *   const pamP,
+         tuple          const tuple,
+         unsigned int   const alphaPlane,
+         sample         const alphaThreshold, 
+         struct cmap *  const cmapP) {
+/*----------------------------------------------------------------------------
+   Return as *colorIndexP the colormap index of the tuple 'tuple',
+   whose format is described by *pamP, using colormap *cmapP.
+
+   'alphaThreshold' is the alpha level below which we consider a
+   pixel transparent for GIF purposes.
+-----------------------------------------------------------------------------*/
+    int colorIndex;
+
+    if (alphaPlane && tuple[alphaPlane] < alphaThreshold &&
+        cmapP->haveTransparent)
+        colorIndex = cmapP->transparent;
+    else {
+        int found;
+
+        pnm_lookuptuple(pamP, cmapP->tuplehash, tuple,
+                        &found, &colorIndex);
+        
+        if (!found)
+            colorIndex = closestColor(tuple, pamP, cmapP);
+    }
+    assert(colorIndex >= 0);
+    return (unsigned int) colorIndex;
+}
+
+
+
+static void
+writeTransparentColorIndexExtension(FILE *       const ofP,
+                                    unsigned int const transColorIndex) {
+/*----------------------------------------------------------------------------
+   Write out extension for transparent color index.
+-----------------------------------------------------------------------------*/
+    fputc('!',  ofP);
+    fputc(0xf9, ofP);
+    fputc(4,    ofP);
+    fputc(1,    ofP);
+    fputc(0,    ofP);
+    fputc(0,    ofP);
+    fputc(transColorIndex, ofP);
+    fputc(0,    ofP);
+}
+
+
+
+static void
+writeCommentExtension(FILE * const ofP,
+                      char   const comment[]) {
+/*----------------------------------------------------------------------------
+   Write out extension for a comment
+-----------------------------------------------------------------------------*/
+    unsigned int const maxSegmentSize = 255;
+
+    const char * segment;
+    
+    fputc('!',  ofP);   /* Identifies an extension */
+    fputc(0xfe, ofP);   /* Identifies a comment */
+
+    /* Write it out in segments no longer than 255 characters */
+    for (segment = &comment[0]; 
+         segment < comment + strlen(comment); 
+         segment += maxSegmentSize) {
+
+        unsigned int const lengthThisSegment =
+            MIN(maxSegmentSize, strlen(segment));
+
+        fputc(lengthThisSegment, ofP);
+
+        fwrite(segment, 1, lengthThisSegment, ofP);
+    }
+
+    fputc(0, ofP);   /* No more comment blocks in this extension */
+}
+
+
+
+/***************************************************************************
+ *
+ *  GIFCOMPR.C       - GIF Image compression routines
+ *
+ *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
+ *  David Rowley (mgardi@watdcsu.waterloo.edu)
+ *
+ ***************************************************************************/
+
+/*
+ * General DEFINEs
+ */
+
+#define BITS    12
+
+/*
+ *
+ * GIF Image compression - modified 'compress'
+ *
+ * Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
+ *              Jim McKie               (decvax!mcvax!jim)
+ *              Steve Davies            (decvax!vax135!petsd!peora!srd)
+ *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
+ *              James A. Woods          (decvax!ihnp4!ames!jaw)
+ *              Joe Orost               (decvax!vax135!petsd!joe)
+ *
+ */
+
+
+static stringCode const maxCodeLimitLzw = (stringCode)1 << BITS;
+       /* One beyond the largest string code that can exist in GIF */ 
+       /* Used only in assertions  */
+
+
+struct hashTableEntry {
+    stringCode fcode;   /* -1 means unused slot */
+    unsigned int ent;
+};    
+
+
+
+/***************************************************************************
+*                          BYTE OUTPUTTER                 
+***************************************************************************/
+
+typedef struct {
+    FILE * fileP;  /* The file to which to output */
+    unsigned int count;
+        /* Number of bytes so far in the current data block */
+    unsigned char buffer[256];
+        /* The current data block, under construction */
+} byteBuffer;
+
+
+
+static byteBuffer *
+byteBuffer_create(FILE * const fileP) {
+
+    byteBuffer * byteBufferP;
+
+    MALLOCVAR_NOFAIL(byteBufferP);
+
+    byteBufferP->fileP = fileP;
+    byteBufferP->count = 0;
+
+    return byteBufferP;
+}
+
+
+
+static void
+byteBuffer_destroy(byteBuffer * const byteBufferP) {
+
+    free(byteBufferP);
+}
+
+
+
+static void
+byteBuffer_flush(byteBuffer * const byteBufferP) {
+/*----------------------------------------------------------------------------
+   Write the current data block to the output file, then reset the current 
+   data block to empty.
+-----------------------------------------------------------------------------*/
+    if (byteBufferP->count > 0 ) {
+        if (verbose)
+            pm_message("Writing %u byte block", byteBufferP->count);
+        fputc(byteBufferP->count, byteBufferP->fileP);
+        fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP);
+        byteBufferP->count = 0;
+    }
+}
+
+
+
+static void
+byteBuffer_flushFile(byteBuffer * const byteBufferP) {
+    
+    fflush(byteBufferP->fileP);
+    
+    if (ferror(byteBufferP->fileP))
+        pm_error("error writing output file");
+}
+
+
+
+static void
+byteBuffer_out(byteBuffer *  const byteBufferP,
+               unsigned char const c) {
+/*----------------------------------------------------------------------------
+  Add a byte to the end of the current data block, and if it is now 254
+  characters, flush the data block to the output file.
+-----------------------------------------------------------------------------*/
+    byteBufferP->buffer[byteBufferP->count++] = c;
+    if (byteBufferP->count >= 254)
+        byteBuffer_flush(byteBufferP);
+}
+
+
+
+/***************************************************************************
+*                          GIF CODE OUTPUTTER                 
+***************************************************************************/
+
+typedef struct {
+    byteBuffer * byteBufferP;
+    unsigned int initBits;
+    unsigned int nBits;
+        /* Number of bits to put in output for each code */
+    stringCode maxCode;                  /* maximum code, given n_bits */
+    stringCode maxCodeLimit;
+        /* LZW: One beyond the largest string code that can exist in GIF.
+           Uncompressed: a ceiling to prevent code size from ratcheting up.
+           In either case, output code never reaches this value.
+        */  
+    unsigned long curAccum;
+    int curBits;
+    unsigned int codeCount;
+        /* Number of codes that have been output to this buffer (doesn't
+           matter if they have gone out the other side yet or not) since
+           the last flush (or ever, if no last flush).  The main use of this
+           is debugging -- when something fails, you can see in a debugger
+           where in the image it was, then set a trap for there.
+        */
+} codeBuffer;
+
+
+
+static codeBuffer *
+codeBuffer_create(FILE *       const ofP,
+                  unsigned int const initBits,
+                  bool         const lzw) {
+
+    codeBuffer * codeBufferP;
+
+    MALLOCVAR_NOFAIL(codeBufferP);
+
+    codeBufferP->initBits    = initBits;
+    codeBufferP->nBits       = codeBufferP->initBits;
+    codeBufferP->maxCode     = (1 << codeBufferP->nBits) - 1;
+    codeBufferP->maxCodeLimit = lzw ?
+        (stringCode)1 << BITS : (stringCode) (1 << codeBufferP->nBits) - 1; 
+    codeBufferP->byteBufferP = byteBuffer_create(ofP);
+    codeBufferP->curAccum    = 0;
+    codeBufferP->curBits     = 0;
+    codeBufferP->codeCount   = 0;
+
+    return codeBufferP;
+}
+
+
+
+static void
+codeBuffer_destroy(codeBuffer * const codeBufferP) {
+
+    byteBuffer_destroy(codeBufferP->byteBufferP);
+
+    free(codeBufferP);
+}
+
+
+
+static void
+codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) {
+
+    codeBufferP->nBits = codeBufferP->initBits;
+
+    assert(codeBufferP->nBits <= BITS);
+
+    codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1;
+}
+
+
+
+static void
+codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) {
+
+    ++codeBufferP->nBits;
+
+    assert(codeBufferP->nBits <= BITS);
+
+    codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1;
+}
+
+static void
+codeBuffer_output(codeBuffer * const codeBufferP,
+                  stringCode   const code) {
+/*----------------------------------------------------------------------------
+   Output one GIF code to the file, through the code buffer.
+
+   The code is represented as N bits in the file -- the lower
+   N bits of 'code'.  N is a the current code size of *codeBufferP.
+   
+   Id 'code' is the maximum possible code for the current code size
+   for *codeBufferP, increase that code size (unless it's already 
+   maxed out).
+-----------------------------------------------------------------------------*/
+    assert (code <= codeBufferP->maxCode);
+
+    codeBufferP->curAccum &= (1 << codeBufferP->curBits) - 1;
+
+    if (codeBufferP->curBits > 0)
+        codeBufferP->curAccum |= ((unsigned long)code << codeBufferP->curBits);
+    else
+        codeBufferP->curAccum = code;
+
+    codeBufferP->curBits += codeBufferP->nBits;
+
+    while (codeBufferP->curBits >= 8) {
+        byteBuffer_out(codeBufferP->byteBufferP,
+                       codeBufferP->curAccum & 0xff);
+        codeBufferP->curAccum >>= 8;
+        codeBufferP->curBits -= 8;
+    }
+
+    ++codeBufferP->codeCount;
+}
+
+
+
+static void
+codeBuffer_flush(codeBuffer * const codeBufferP) {
+
+    /* Output the possible partial byte in the buffer */
+
+    if (codeBufferP->curBits > 0) {
+        byteBuffer_out(codeBufferP->byteBufferP,
+                       codeBufferP->curAccum & 0xff);
+        codeBufferP->curBits = 0;
+    }
+    byteBuffer_flush(codeBufferP->byteBufferP);
+    
+    byteBuffer_flushFile(codeBufferP->byteBufferP);
+
+    if (verbose)
+        pm_message("%u strings of pixels written to file",
+                   codeBufferP->codeCount);
+    codeBufferP->codeCount = 0;
+}
+
+
+
+typedef struct {
+    codeBuffer * codeBufferP;
+        /* The place to which we write our string codes.
+
+           Constant.
+        */
+    bool lzw;
+        /* We're actually doing LZW compression.  False means we follow
+           the algorithm enough tht an LZW decompressor will recover the
+           proper data, but always using one code per pixel, and therefore
+           not effecting any compression and not using the LZW patent.
+        */
+    unsigned int hsize;
+        /* The number of slots in the hash table.  This variable to
+           enhance overall performance by reducing memory use when
+           encoding smaller gifs. 
+         */
+        
+    unsigned int hshift;
+        /* This is how many bits we shift left a string code in forming the
+           primary hash of the concatenation of that string with another.
+           Constant.
+        */
+
+    /* Codes less than 'clearCode' are singleton pixel codes - each
+       represents the pixel value equal to it.
+
+       Codes greater than 'eofCode' are multipixel string codes.  Each
+       represents a string of pixels that is defined by the preceding
+       stream.
+    */
+    stringCode clearCode;
+        /* The code in an LZW stream that means to clear the string
+           dictionary and start fresh.
+
+           Constant.
+        */
+    stringCode eofCode;
+        /* The code in an LZW stream that means there's no more coming
+
+           Constant.
+        */
+    stringCode initCodeLimit;
+        /* The value of 'codeLimit' at the start of a block.
+
+           Constant.
+        */
+
+    stringCode codeLimit;
+        /* One beyond the maximum code possible with the current code
+           size.
+        */
+
+    struct hashTableEntry * hashTable;
+    stringCode nextUnusedCode;
+        /* Numerically next code available to assign to a a multi-pixel
+           string.  Note that codes for multi-pixel strings are in the
+           upper half of the range of codes, always greater than
+           'clearCode'.
+        */
+
+    stringCode stringSoFar;
+        /* The code for the string we have built so far.  This code indicates
+           one or more pixels that we have encoded but not yet output
+           because we're hoping to match an even longer string.
+
+           Valid only when 'buildingString' is true.
+
+           In the non-lzw case the single pixel to output.
+        */
+    bool buildingString;
+        /* We are in the middle of building a string; 'stringSoFar' describes
+           the pixels in it so far.  The only time this is false is at the
+           very beginning of the stream.
+ 
+           Ignored in the non-lzw case. 
+        */
+} lzwCompressor;
+
+
+
+
+static unsigned int
+nSignificantBits( unsigned int const arg ){
+
+#if HAVE_GCC_BITCOUNT
+
+    return (arg == 0) ? 0 : 8 * sizeof(unsigned int) - __builtin_clz(arg);
+
+#else
+
+    unsigned int i = 0;
+    while (arg >> i != 0)
+        ++i;
+
+    return i;
+#endif
+}
+
+
+
+static lzwCompressor *
+lzw_create(FILE *       const ofP,
+           unsigned int const initBits,
+           bool         const lzw,
+           unsigned int const pixelCount) {
+
+    unsigned int const hsizeTable[] = {257, 521, 1031, 2053, 4099, 5003};
+    /* If the image has 4096 or fewer pixels we use prime numbers slightly
+       above powers of two between 8 and 12.  In this case the hash table
+       never fills up; clear code is never emitted.
+    
+       Above that we use a table with 4096 slots plus 20% extra.
+       When this is not enough the clear code is emitted.
+       Due to the extra 20% the table itself never fills up.
+       
+       lzw.hsize and lzw.hshift stay constant through the image.
+
+       Variable hsize is a performance enhancement based on the fact that
+       the encoder never needs more codes than the number of pixels in
+       the image.  Typically, the ratio of pixels to codes is around
+       10:1 to 20:1.
+   
+       Logic works with fixed values lzw.hsize=5003 and t=13.
+    */
+
+    lzwCompressor * lzwP;
+       
+    MALLOCVAR_NOFAIL(lzwP);
+
+    /* Constants */
+    lzwP->lzw = lzw;
+
+    lzwP->clearCode     = 1 << (initBits - 1);
+    lzwP->eofCode       = lzwP->clearCode + 1;
+    lzwP->initCodeLimit = 1 << initBits;
+
+    if (lzw) {
+        unsigned int const t =
+            MIN(13, MAX(8, nSignificantBits(pixelCount +lzwP->eofCode - 2)));
+            /* Index into hsizeTable */
+    
+        lzwP->hsize = hsizeTable[t-8];
+
+        lzwP->hshift = (t == 13 ? 12 : t) - nSignificantBits(MAXCMAPSIZE-1);
+
+        MALLOCARRAY(lzwP->hashTable, lzwP->hsize);
+        
+        if (lzwP->hashTable == NULL)
+            pm_error("Couldn't get memory for %u-entry hash table.",
+                     lzwP->hsize);
+    } else {
+        /* No LZW compression.  We don't need a stringcode hash table */  
+        lzwP->hashTable = NULL;
+        lzwP->hsize     = 0;
+    }
+
+    lzwP->buildingString = FALSE;
+
+    lzwP->codeBufferP = codeBuffer_create(ofP, initBits, lzw);
+
+    return lzwP;
+}
+
+
+
+static void
+lzw_destroy(lzwCompressor * const lzwP) {
+
+    codeBuffer_destroy(lzwP->codeBufferP);
+
+    free(lzwP->hashTable);
+
+    free(lzwP);
+}
+
+
+
+static void
+lzwHashClear(lzwCompressor * const lzwP) {
+
+    /* Empty the code table */
+
+    unsigned int i;
+
+    for (i = 0; i < lzwP->hsize; ++i)
+        lzwP->hashTable[i].fcode = -1;
+
+    lzwP->nextUnusedCode = lzwP->clearCode + 2;
+}
+
+
+
+static void
+lzw_clearBlock(lzwCompressor * const lzwP) {
+/*----------------------------------------------------------------------------
+  
+-----------------------------------------------------------------------------*/
+    lzwHashClear(lzwP);
+
+    codeBuffer_output(lzwP->codeBufferP, lzwP->clearCode);
+
+    codeBuffer_resetCodeSize(lzwP->codeBufferP);
+
+    lzwP->codeLimit = lzwP->initCodeLimit;
+}
+
+
+
+static void
+lzwAdjustCodeSize(lzwCompressor * const lzwP,
+                  stringCode      const newCode) {
+/*----------------------------------------------------------------------------
+   Assuming we just defined code 'newCode', increase the code size as
+   required so that this code fits.
+
+   The decompressor is mimicking our assignment of that code, so knows that
+   we are making this adjustment, so expects codes of the new size.
+-----------------------------------------------------------------------------*/
+    assert(newCode <= lzwP->codeLimit);
+
+    if (newCode == lzwP->codeLimit) {
+        lzwP->codeLimit *= 2;
+        codeBuffer_increaseCodeSize(lzwP->codeBufferP);
+
+        assert(lzwP->codeLimit <= maxCodeLimitLzw);
+    }
+}
+
+
+
+static void
+lzwOutputCurrentString(lzwCompressor * const lzwP) {
+/*----------------------------------------------------------------------------
+   Put a code for the currently built-up string in the output stream.
+
+   Doing this causes a new string code to be defined (code is
+   lzwP->nextUnusedCode), so Caller must add that to the hash.  If
+   that code's size is beyond the overall limit, we reset the hash
+   (which means future codes will start back at the minimum size) and
+   put a clear code in the stream to tell the decompressor to do the
+   same.  So Caller must add it to the hash _before_ calling us.
+
+   Note that in the non-compressing case, the overall limit is small
+   enough to prevent us from ever defining string codes; we'll always
+   reset the hash.
+
+   There's an odd case that always screws up any attempt to make this
+   code cleaner: At the end of the LZW stream, you have to output the
+   code for the final string even though you don't have a following
+   pixel that would make a longer string.  So there's nothing to add
+   to the hash table and no point in allocating a new string code.
+   But the decompressor doesn't know that we're done, so he allocates
+   the next string code and may therefore increase his code length.
+   If we don't do the same, we will write our one last code -- the EOF
+   code -- in a code length smaller than what the decompressor is
+   expecting, and he will have a premature end of stream.
+
+   So this subroutine does run for that final code flush and does some
+   of the motions of defining a new string code, but this subroutine
+   can't update the hash because in that particular case, there's
+   nothing to add.
+-----------------------------------------------------------------------------*/
+    codeBuffer_output(lzwP->codeBufferP, lzwP->stringSoFar);
+    if (lzwP->nextUnusedCode < lzwP->codeBufferP->maxCodeLimit) {
+        /* Allocate the code for the extended string, which Caller
+           should have already put in the hash so he can use it in the
+           future.  Decompressor knows when it sees the code output
+           above to define a string on its end too, using the same
+           string code we do.
+        */
+        stringCode const newCode = lzwP->nextUnusedCode++;
+
+        /* This code may be too big to fit in the current code size, in
+           which case we have to increase the code size (and decompressor
+           will do the same).
+        */
+        lzwAdjustCodeSize(lzwP, newCode);
+    } else {
+        /* Forget all the strings so far; start building again; tell
+           decompressor to do the same.
+        */
+        lzw_clearBlock(lzwP);
+    }
+}
+
+
+
+static void
+lzw_flush(lzwCompressor * const lzwP) {
+
+    if (lzwP->lzw)
+        lzwOutputCurrentString(lzwP);
+        /* Put out the code for the final string. */
+
+    codeBuffer_output(lzwP->codeBufferP, lzwP->eofCode);
+
+    codeBuffer_flush(lzwP->codeBufferP);
+}
+
+
+
+static unsigned int
+primaryHash(stringCode   const baseString,
+            stringCode   const additionalPixel,
+            unsigned int const hshift) {
+
+    unsigned int hash;
+
+    assert(baseString < maxCodeLimitLzw);
+    assert(additionalPixel < MAXCMAPSIZE);
+
+    hash = (additionalPixel << hshift) ^ baseString;
+    
+    return hash;
+}
+
+    
+
+static void
+lookupInHash(lzwCompressor *  const lzwP,
+             unsigned int     const gifPixel,
+             stringCode       const fcode,
+             bool *           const foundP,
+             unsigned short * const codeP,
+             unsigned int *   const hashP) {
+
+    int disp;
+        /* secondary hash stride (after G. Knott) */
+    int i;
+        /* Index into hash table */
+
+    i = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift);
+    disp = (i == 0) ? 1 : lzwP->hsize - i;
+
+    while (lzwP->hashTable[i].fcode != fcode &&
+           lzwP->hashTable[i].fcode >= 0) {
+        i -= disp;
+        if (i < 0)
+            i += lzwP->hsize;
+    }
+
+    if (lzwP->hashTable[i].fcode == fcode) {
+        /* Found fcode in hash table */
+        *foundP = TRUE;
+        *codeP = lzwP->hashTable[i].ent;
+    } else {
+        /* Found where it _should_ be (but it's not) with primary hash */
+        *foundP = FALSE;
+        *hashP = i;
+    }
+}
+
+
+
+static void
+lzw_encodePixel(lzwCompressor * const lzwP,
+                unsigned int    const gifPixel) {
+
+    bool found;
+    unsigned short code;
+    unsigned int hash;
+        /* Index into hash table where the value should go */
+    
+    assert(gifPixel < 256);
+
+    if (!lzwP->buildingString) {
+        /* Start a new string with just this pixel */
+        lzwP->stringSoFar = gifPixel;
+        lzwP->buildingString = TRUE;
+    } else {
+        stringCode const fcode =
+            ((stringCode) gifPixel << BITS) + lzwP->stringSoFar;
+            /* The encoding of the string we've already recognized plus the
+               instant pixel, to be looked up in the hash of known strings.
+            */
+    
+        lookupInHash(lzwP, gifPixel, fcode, &found, &code, &hash);
+
+        if (found)
+            /* With this new pixel, it is still a known string; 'code' is
+               its code
+            */
+            lzwP->stringSoFar = code;
+        else {
+            /* It's no longer a known string.  Output the code for the
+               known prefix of the string, thus defining a new string
+               code for possible later use.  Warning:
+               lzwOutputCurrentString() does more than you think. 
+            */
+
+            lzwP->hashTable[hash].ent   = lzwP->nextUnusedCode;
+            lzwP->hashTable[hash].fcode = fcode;
+
+            lzwOutputCurrentString(lzwP);
+
+            /* This singleton pixel starts the next string */
+            lzwP->stringSoFar = gifPixel;
+        }
+    }
+}
+
+
+
+/*
+ * compress stdin to stdout
+ *
+ * Algorithm:  use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination.  We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe.  Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation.  Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills.  The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor.  Late addition:  construct the table according to
+ * file size for noticeable speed improvement on small files.  Please direct
+ * questions about this implementation to ames!jaw.
+ */
+
+static void
+writePixelUncompressed(lzwCompressor * const lzwP,
+                       unsigned int    const gifPixel) {
+                      
+    lzwP->stringSoFar = gifPixel;
+    lzwOutputCurrentString(lzwP);
+
+}    
+
+static void
+writeRaster(struct pam *  const pamP,
+            rowReader *   const rowReaderP,
+            unsigned int  const alphaPlane,
+            unsigned int  const alphaThreshold,
+            struct cmap * const cmapP, 
+            unsigned int  const initBits,
+            FILE *        const ofP,
+            bool          const lzw) {
+/*----------------------------------------------------------------------------
+   Write the raster to file 'ofP'.
+
+   Get the raster to write from 'rowReaderP', which gives tuples whose
+   format is described by 'pamP'.
+
+   Use the colormap 'cmapP' to generate the raster ('rowReaderP' gives
+   pixel values as RGB samples; the GIF raster is colormap indices).
+
+   Write the raster using LZW compression, or uncompressed depending
+   on 'lzw'.
+-----------------------------------------------------------------------------*/
+    lzwCompressor * lzwP;
+    tuple * tuplerow;
+    unsigned int nRowsDone;
+        /* Number of rows we have read so far from the the input (the
+           last of which is the one we're working on now).  Note that
+           in case of interlace, this is not the same thing as the row
+           number of the current row.
+        */
+    
+    lzwP = lzw_create(ofP, initBits, lzw, pamP->height * pamP->width);
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    lzw_clearBlock(lzwP);
+
+    nRowsDone = 0;
+    
+    while (nRowsDone < pamP->height) {
+        unsigned int col;
+
+        rowReader_read(rowReaderP, tuplerow);
+
+        for (col = 0; col < pamP->width; ++col) {
+            unsigned int const colorIndex =
+                gifPixel(pamP, tuplerow[col], alphaPlane, alphaThreshold,
+                         cmapP);
+            
+                /* The value for the pixel in the GIF image.  I.e. the colormap
+                   index.
+                */
+            if (lzw)
+                lzw_encodePixel(lzwP, colorIndex);
+            else
+                writePixelUncompressed(lzwP, colorIndex);    
+        }
+        ++nRowsDone;
+    }
+    /* Gif is no good with no pixels; fortunately, that's impossible: */
+    assert(nRowsDone > 0);
+
+    lzw_flush(lzwP);
+
+    pnm_freepamrow(tuplerow);
+    
+    lzw_destroy(lzwP);
+}
+
+
+
+static void
+writeGlobalColorMap(FILE *              const ofP,
+                    const struct cmap * const cmapP,
+                    unsigned int        const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+  Write out the Global Color Map
+
+  Note that the Global Color Map is always a power of two colors
+  in size, but *cmapP could be smaller than that.  So we pad with
+  black.
+-----------------------------------------------------------------------------*/
+    unsigned int const colorMapSize = 1 << bitsPerPixel;
+
+    struct pam pam;
+    unsigned int i;
+    tuple tupleRgb255;
+
+    if (verbose)
+        pm_message("Writing %u-entry global colormap for %u colors",
+                   colorMapSize, cmapP->cmapSize);
+
+    pam = cmapP->pam;
+    pam.size = PAM_STRUCT_SIZE(allocation_depth);
+    pam.len = pam.size;
+    pnm_setminallocationdepth(&pam, 3);
+
+    tupleRgb255 = pnm_allocpamtuple(&pam);
+
+    for (i = 0; i < colorMapSize; ++i) {
+        if (i < cmapP->cmapSize) {
+            tuple const color = cmapP->color[i];
+
+            assert(i < cmapP->cmapSize);
+
+            pnm_scaletuple(&pam, tupleRgb255, color, 255);
+            pnm_maketuplergb(&pam, tupleRgb255);
+
+            fputc(tupleRgb255[PAM_RED_PLANE], ofP);
+            fputc(tupleRgb255[PAM_GRN_PLANE], ofP);
+            fputc(tupleRgb255[PAM_BLU_PLANE], ofP);
+        } else {
+            fputc(0, ofP);
+            fputc(0, ofP);
+            fputc(0, ofP);
+        }
+    }
+    pnm_freepamtuple(tupleRgb255);
+}
+        
+
+
+static void
+writeGifHeader(FILE *              const ofP,
+               unsigned int        const width,
+               unsigned int        const height, 
+               unsigned int        const background, 
+               unsigned int        const bitsPerPixel,
+               const struct cmap * const cmapP,
+               char                const comment[],
+               float               const aspect) {
+
+    unsigned int const resolution = bitsPerPixel;
+
+    unsigned char b;
+
+    /* Write the Magic header */
+    if (cmapP->haveTransparent || comment || aspect != 1.0 )
+        fwrite("GIF89a", 1, 6, ofP);
+    else
+        fwrite("GIF87a", 1, 6, ofP);
+
+    /* Write out the screen width and height */
+    Putword(width,  ofP);
+    Putword(height, ofP);
+
+    /* Indicate that there is a global color map */
+    b = 0x80;       /* Yes, there is a color map */
+
+    /* OR in the resolution */
+    b |= (resolution - 1) << 4;
+
+    /* OR in the Bits per Pixel */
+    b |= (bitsPerPixel - 1);
+
+    /* Write it out */
+    fputc(b, ofP);
+
+    /* Write out the Background color */
+    assert((unsigned char)background == background);
+    fputc(background, ofP);
+
+    {
+        int const aspectValue = aspect == 1.0 ? 0 : ROUND(aspect * 64) - 15;
+        assert(0 <= aspectValue && aspectValue <= 255); 
+        fputc(aspectValue, ofP);
+    }
+    writeGlobalColorMap(ofP, cmapP, bitsPerPixel);
+
+    if (cmapP->haveTransparent) 
+        writeTransparentColorIndexExtension(ofP, cmapP->transparent);
+
+    if (comment)
+        writeCommentExtension(ofP, comment);
+}
+
+
+
+static void
+writeImageHeader(FILE *       const ofP,
+                 unsigned int const leftOffset,
+                 unsigned int const topOffset,
+                 unsigned int const gWidth,
+                 unsigned int const gHeight,
+                 bool         const gInterlace,
+                 unsigned int const initCodeSize) {
+
+    Putword(leftOffset, ofP);
+    Putword(topOffset,  ofP);
+    Putword(gWidth,     ofP);
+    Putword(gHeight,    ofP);
+
+    /* Write out whether or not the image is interlaced */
+    if (gInterlace)
+        fputc(0x40, ofP);
+    else
+        fputc(0x00, ofP);
+
+    /* Write out the initial code size */
+    fputc(initCodeSize, ofP);
+}
+
+
+
+static void
+reportImageInfo(bool         const interlace,
+                unsigned int const background,
+                unsigned int const bitsPerPixel) {
+
+    if (verbose) {
+        if (interlace)
+            pm_message("interlaced");
+        else
+            pm_message("not interlaced");
+        pm_message("Background color index = %u", background);
+        pm_message("%u bits per pixel", bitsPerPixel);
+    }
+}
+
+
+
+static void
+gifEncode(struct pam *  const pamP,
+          FILE *        const ofP, 
+          pm_filepos    const rasterPos,
+          bool          const gInterlace,
+          int           const background, 
+          unsigned int  const bitsPerPixel,
+          struct cmap * const cmapP,
+          char          const comment[],
+          float         const aspect,
+          bool          const lzw) {
+
+    unsigned int const leftOffset = 0;
+    unsigned int const topOffset  = 0;
+
+    unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel;
+        /* The initial code size */
+
+    sample const alphaThreshold = (pamP->maxval + 1) / 2;
+        /* Levels below this in the alpha plane indicate transparent
+           pixels in the output image.
+        */
+
+    unsigned int const alphaPlane = pamAlphaPlane(pamP);
+
+    rowReader * rowReaderP;
+
+    reportImageInfo(gInterlace, background, bitsPerPixel);
+
+    if (pamP->width > 65535)
+        pm_error("Image width %u too large for GIF format.  (Max 65535)",
+                 pamP->width);
+     
+    if (pamP->height > 65535)
+        pm_error("Image height %u too large for GIF format.  (Max 65535)",
+                 pamP->height);
+
+    writeGifHeader(ofP, pamP->width, pamP->height, background,
+                   bitsPerPixel, cmapP, comment, aspect);
+
+    /* Write an Image separator */
+    fputc(',', ofP);
+
+    writeImageHeader(ofP, leftOffset, topOffset, pamP->width, pamP->height,
+                     gInterlace, initCodeSize);
+
+    rowReaderP = rowReader_create(pamP, rasterPos, gInterlace);
+
+    /* Write the actual raster */
+
+    writeRaster(pamP, rowReaderP, alphaPlane, alphaThreshold,
+                cmapP, initCodeSize + 1, ofP, lzw);
+
+    rowReader_destroy(rowReaderP);
+
+    /* Write out a zero length data block (to end the series) */
+    fputc(0, ofP);
+
+    /* Write the GIF file terminator */
+    fputc(';', ofP);
+}
+
+
+
+static void
+reportTransparent(struct cmap * const cmapP) {
+
+    if (verbose) {
+        if (cmapP->haveTransparent) {
+            tuple const color = cmapP->color[cmapP->transparent];
+            pm_message("Color %u (%lu, %lu, %lu) is transparent",
+                       cmapP->transparent,
+                       color[PAM_RED_PLANE],
+                       color[PAM_GRN_PLANE],
+                       color[PAM_BLU_PLANE]);
+        } else
+            pm_message("No transparent color");
+    }
+}
+
+
+
+static void
+computeTransparent(char          const colorarg[], 
+                   bool          const usingFakeTrans,
+                   unsigned int  const fakeTransparent,
+                   struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Figure out the color index (index into the colormap) of the color
+   that is to be transparent in the GIF.
+
+   colorarg[] is the string that specifies the color the user wants to
+   be transparent (e.g. "red", "#fefefe").  Its maxval is the maxval
+   of the colormap.  'cmap' is the full colormap except that its
+   'transparent' component isn't valid.
+
+   colorarg[] is a standard Netpbm color specification, except that
+   may have a "=" prefix, which means it specifies a particular exact
+   color, as opposed to without the "=", which means "the color that
+   is closest to this and actually in the image."
+
+   colorarg[] null means the color didn't ask for a particular color
+   to be transparent.
+
+   Establish no transparent color if colorarg[] specifies an exact
+   color and that color is not in the image.  Also issue an
+   informational message.
+
+   'usingFakeTrans' means pixels will be transparent because of something
+   other than their foreground color, and 'fakeTransparent' is the
+   color map index for transparent colors.
+-----------------------------------------------------------------------------*/
+    if (colorarg) {
+        const char * colorspec;
+        bool exact;
+        tuple transcolor;
+        bool found;
+        int colorindex;
+        
+        if (colorarg[0] == '=') {
+            colorspec = &colorarg[1];
+            exact = TRUE;
+        } else {
+            colorspec = colorarg;
+            exact = FALSE;
+        }
+
+        transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval);
+        pnm_lookuptuple(&cmapP->pam, cmapP->tuplehash, transcolor, &found,
+                        &colorindex);
+        
+        if (found) {
+            cmapP->haveTransparent = TRUE;
+            cmapP->transparent = colorindex;
+        } else if (!exact) {
+            cmapP->haveTransparent = TRUE;
+            cmapP->transparent = closestColor(transcolor, &cmapP->pam, cmapP);
+        } else {
+            cmapP->haveTransparent = FALSE;
+            pm_message("Warning: specified transparent color "
+                       "does not occur in image.");
+        }
+    } else if (usingFakeTrans) {
+        cmapP->haveTransparent = TRUE;
+        cmapP->transparent = fakeTransparent;
+    } else
+        cmapP->haveTransparent = FALSE;
+
+    reportTransparent(cmapP);
+}
+
+
+
+static unsigned int
+sortOrderColor(tuple const tuple) {
+
+    return ((tuple[PAM_RED_PLANE] * MAXCMAPSIZE) +
+            tuple[PAM_GRN_PLANE]) * MAXCMAPSIZE +
+           tuple[PAM_BLU_PLANE];
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn sortCompareColor;
+#endif
+
+static int
+sortCompareColor(const void * const entry1P,
+                 const void * const entry2P) {
+
+    struct tupleint * const * const tupleint1PP = entry1P;
+    struct tupleint * const * const tupleint2PP = entry2P;
+
+    return (sortOrderColor((*tupleint1PP)->tuple) 
+            - sortOrderColor((*tupleint2PP)->tuple));
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn sortCompareGray;
+#endif
+
+static int
+sortCompareGray(const void * const entry1P,
+                const void * const entry2P){
+
+    struct tupleint * const * const tupleint1PP = entry1P;
+    struct tupleint * const * const tupleint2PP = entry2P;
+
+    return ((*tupleint1PP)->tuple[0] - (*tupleint2PP)->tuple[0]);
+}
+
+
+
+static void
+sortTupletable(struct pam * const mapPamP,
+               unsigned int const colors,
+               tupletable   const tuplefreq) {
+/*----------------------------------------------------------------------------
+   Sort the colormap *cmapP.
+
+   Sort the colormap by red intensity, then by green intensity,
+   then by blue intensity.
+-----------------------------------------------------------------------------*/
+
+    pm_message("sorting colormap");
+
+    if (mapPamP->depth < 3)
+        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareGray);
+    else
+        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareColor); 
+
+}
+
+
+
+static void
+addToColormap(struct cmap *  const cmapP, 
+              const char *   const colorspec, 
+              unsigned int * const newIndexP) {
+/*----------------------------------------------------------------------------
+  Add a new entry to the colormap.  Make the color that specified by
+  'colorspec', and return the index of the new entry as *newIndexP.
+
+  'colorspec' is a color specification given by the user, e.g.
+  "red" or "rgb:ff/03/0d".  The maxval for this color specification is
+  that for the colormap *cmapP.
+-----------------------------------------------------------------------------*/
+    tuple const transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval);
+
+    unsigned int const colorIndex = cmapP->cmapSize++;
+
+    cmapP->color[colorIndex] = pnm_allocpamtuple(&cmapP->pam);
+
+    if (cmapP->pam.depth < 3) {
+        if (!pnm_rgbtupleisgray(transcolor))
+            pm_error("Image is grayscale, but color '%s' is not gray.  "
+                     "It is (%lu, %lu, %lu)",
+                     colorspec,
+                     transcolor[PAM_RED_PLANE],
+                     transcolor[PAM_GRN_PLANE],
+                     transcolor[PAM_BLU_PLANE]);
+        else
+            cmapP->color[colorIndex][0] = transcolor[0];
+    } else {
+        pnm_assigntuple(&cmapP->pam, cmapP->color[colorIndex], transcolor);
+    }
+    *newIndexP = colorIndex;
+}
+
+
+
+static void
+colormapFromFile(char               const filespec[],
+                 unsigned int       const maxcolors,
+                 tupletable *       const tupletableP, 
+                 struct pam *       const mapPamP,
+                 unsigned int *     const colorCountP) {
+/*----------------------------------------------------------------------------
+   Read a colormap from the Netpbm file filespec[].  Return a
+   tupletable of the colors in it (which is practically a colormap) as
+   *tupletableP and the format of those tuples as *mapPamP.  Return
+   the number of colors as *colorsCountP.
+-----------------------------------------------------------------------------*/
+    FILE * mapfileP;
+    tuple ** colors;
+    unsigned int colorCount;
+
+    mapfileP = pm_openr(filespec);
+    colors = pnm_readpam(mapfileP, mapPamP, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(mapfileP);
+
+    pm_message("computing other colormap ...");
+    
+    *tupletableP = 
+        pnm_computetuplefreqtable(mapPamP, colors, maxcolors, &colorCount);
+
+    *colorCountP = colorCount;
+
+    pnm_freepamarray(colors, mapPamP); 
+}
+
+
+
+static void
+readAndValidateColormapFromFile(char           const filename[],
+                                unsigned int   const maxcolors,
+                                tupletable *   const tuplefreqP, 
+                                struct pam *   const mapPamP,
+                                unsigned int * const colorCountP,
+                                unsigned int   const nInputComp,
+                                sample         const inputMaxval) {
+/*----------------------------------------------------------------------------
+   Read the colormap from a separate colormap file named filename[],
+   and make sure it's consistent with an image with 'nInputComp'
+   color components (e.g. 3 for RGB) and a maxval of 'inputMaxval'.
+-----------------------------------------------------------------------------*/
+    colormapFromFile(filename, maxcolors, tuplefreqP, mapPamP, colorCountP);
+
+    if (mapPamP->depth != nInputComp)
+        pm_error("Depth of map file (%u) does not match number of "
+                 "color components in input file (%u)",
+                 mapPamP->depth, nInputComp);
+    if (mapPamP->maxval != inputMaxval)
+        pm_error("Maxval of map file (%lu) does not match maxval of "
+                 "input file (%lu)", mapPamP->maxval, inputMaxval);
+}
+
+
+
+static void
+computeColormapBw(struct pam *   const pamP,
+                  struct pam *   const mapPamP,
+                  unsigned int * const colorCountP,
+                  tupletable   * const tuplefreqP) {
+/*----------------------------------------------------------------------------
+  Shortcut for black and white (e.g. PBM).  We know that there are
+  only two colors.  Users who know that only one color is present in
+  the image should specify -sort at the command line.  Example:
+
+   $ pbmmake -w 600 400 | pamtogif -sort > canvas.gif
+-----------------------------------------------------------------------------*/
+    tupletable const colormap = pnm_alloctupletable(pamP, 2);
+    
+    *mapPamP = *pamP;
+    mapPamP->depth = 1;
+
+    colormap[0]->value = 1;
+    colormap[0]->tuple[0] = PAM_BLACK;
+    colormap[1]->value = 1;
+    colormap[1]->tuple[0] = PAM_BW_WHITE;
+    
+    *tuplefreqP  = colormap;
+    *colorCountP = 2;
+}
+  
+    
+
+static void
+computeColormapFromInput(struct pam *   const pamP,
+                         unsigned int   const maxcolors,
+                         unsigned int   const nInputComp,
+                         struct pam *   const mapPamP,
+                         unsigned int * const colorCountP,
+                         tupletable *   const tuplefreqP) {
+    
+    tupletable tuplefreq;
+
+    pm_message("computing colormap...");
+
+    tuplefreq = pnm_computetuplefreqtable3(
+        pamP, NULL, maxcolors, nInputComp, pamP->maxval, colorCountP);
+
+    *mapPamP = *pamP;
+    mapPamP->depth = nInputComp;
+
+    *tuplefreqP = tuplefreq;
+}
+
+
+
+static void
+computeLibnetpbmColormap(struct pam *   const pamP,
+                         bool           const haveAlpha,
+                         const char *   const mapfile,
+                         tuple *        const color,
+                         tuplehash *    const tuplehashP,
+                         struct pam *   const mapPamP,
+                         unsigned int * const colorCountP,
+                         bool           const sort) {
+/*----------------------------------------------------------------------------
+   Compute a colormap, libnetpbm style, for the image described by
+   'pamP', which is positioned to the raster.
+
+   If 'mapfile' is non-null, Use the colors in that (Netpbm) file for
+   the color map instead of the colors in 'pamP'.
+
+   Return the colormap as color[] and *tuplehashP.  Return the format
+   of those tuples as *mapPamP.
+
+   The tuples of the color map have a meaningful depth of 1 (grayscale) or 3
+   (color) and *mapPamP reflects that.
+
+   While we're at it, count the colors and validate that there aren't
+   too many.  Return the count as *colorCountP.  In determining if there are
+   too many, allow one slot for a fake transparency color if 'haveAlpha'
+   is true.  If there are too many, issue an error message and abort the
+   program.
+
+   'sort' means to sort the colormap by red intensity, then by green
+   intensity, then by blue intensity, as opposed to arbitrary order.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxcolors = haveAlpha ? MAXCMAPSIZE - 1 : MAXCMAPSIZE;
+        /* The most colors we can tolerate in the image.  If we have
+           our own made-up entry in the colormap for transparency, it
+           isn't included in this count.
+        */
+    unsigned int const nInputComp = haveAlpha ? pamP->depth - 1 : pamP->depth;
+        /* Number of color components (not alpha) in the input image */
+
+    unsigned int i;
+    tupletable tuplefreq;
+    unsigned int colorCount;
+
+    if (mapfile)
+        readAndValidateColormapFromFile(mapfile, maxcolors, &tuplefreq,
+                                        mapPamP, &colorCount,
+                                        nInputComp, pamP->maxval);
+    else if (nInputComp == 1 && pamP->maxval == 1 && !sort &&
+             pamP->height * pamP->width > 1)
+        computeColormapBw(pamP, mapPamP, &colorCount, &tuplefreq);
+    else
+        computeColormapFromInput(pamP, maxcolors, nInputComp, 
+                                 mapPamP, &colorCount, &tuplefreq);
+    
+    if (tuplefreq == NULL)
+        pm_error("too many colors - try doing a 'pnmquant %u'", maxcolors);
+
+    pm_message("%u colors found", colorCount);
+
+    if (sort)
+        sortTupletable(mapPamP, colorCount, tuplefreq);
+
+    for (i = 0; i < colorCount; ++i) {
+        color[i] = pnm_allocpamtuple(mapPamP);
+        pnm_assigntuple(mapPamP, color[i], tuplefreq[i]->tuple);
+    }
+
+    /* And make a hash table for fast lookup. */
+    *tuplehashP =
+        pnm_computetupletablehash(mapPamP, tuplefreq, colorCount);
+
+    *colorCountP = colorCount;
+
+    pnm_freetupletable(mapPamP, tuplefreq);
+}
+
+
+
+static void
+destroyCmap(struct cmap * const cmapP) {
+
+    unsigned int colorIndex;
+    
+    for (colorIndex = 0; colorIndex < cmapP->cmapSize; ++colorIndex)
+        pnm_freepamtuple(cmapP->color[colorIndex]);
+
+    pnm_destroytuplehash(cmapP->tuplehash);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam pam;
+    unsigned int bitsPerPixel;
+    pm_filepos rasterPos;
+
+    struct cmap cmap;
+        /* The colormap, with all its accessories */
+    unsigned int fakeTransparent;
+        /* colormap index of the fake transparency color we're using to
+           implement the alpha mask.  Undefined if we're not doing an alpha
+           mask.
+        */
+    
+    pnm_init(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+    
+    verbose = cmdline.verbose;
+    
+    ifP = pm_openr_seekable(cmdline.input_filespec);
+    
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+    
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+    
+    computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile, 
+                             cmap.color, &cmap.tuplehash,
+                             &cmap.pam, &cmap.cmapSize, cmdline.sort);
+    
+    assert(cmap.pam.maxval == pam.maxval);
+
+    if (pamAlphaPlane(&pam)) {
+        /* Add a fake entry to the end of the colormap for transparency.  
+           Make its color black. 
+        */
+        addToColormap(&cmap, cmdline.alphacolor, &fakeTransparent);
+    }
+
+    bitsPerPixel = cmap.cmapSize == 1 ? 1 : nSignificantBits(cmap.cmapSize-1);
+
+    computeTransparent(cmdline.transparent,
+                       !!pamAlphaPlane(&pam), fakeTransparent, &cmap);
+
+    /* All set, let's do it. */
+    gifEncode(&pam, stdout, rasterPos,
+              cmdline.interlace, 0, bitsPerPixel, &cmap, cmdline.comment,
+              cmdline.aspect, !cmdline.nolzw);
+    
+    destroyCmap(&cmap);
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
+
+
+
+/*============================================================================
+  Original version, named 'ppmgif' was by Jef Poskanzer in 1989, based
+  on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A Lempel-Zim
+  compression based on "compress".
+
+  Switched to use libnetpbm PAM facilities (ergo process PAM images)
+  and renamed 'pamtogif' by Bryan Henderson November 2006.
+
+  The non-LZW GIF generation stuff was adapted from the Independent
+  JPEG Group's djpeg on 2001.09.29.  In 2006.12 the output subroutines
+  were rewritten; now no uncompressed output subroutines are derived from
+  the Independent JPEG Group's source code.
+  
+  2007.01  Changed sort routine to qsort.  (afu)
+  2007.03  Implemented variable hash table size, PBM color table
+           shortcut and "-aspect" command line option.   (afu)
+
+ 
+  Copyright (C) 1989 by Jef Poskanzer.
+ 
+  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.
+ 
+  The Graphics Interchange Format(c) is the Copyright property of
+  CompuServe Incorporated.  GIF(sm) is a Service Mark property of
+  CompuServe Incorporated.
+============================================================================*/
+
diff --git a/converter/other/pamtohdiff.c b/converter/other/pamtohdiff.c
index 0e1ff00f..2d5f6a61 100644
--- a/converter/other/pamtohdiff.c
+++ b/converter/other/pamtohdiff.c
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 
diff --git a/converter/other/pamtohtmltbl.c b/converter/other/pamtohtmltbl.c
index 0afbfea0..5335ff9f 100644
--- a/converter/other/pamtohtmltbl.c
+++ b/converter/other/pamtohtmltbl.c
@@ -2,16 +2,17 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "pam.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* '-' if stdin */
-    const char *transparent;  /* NULL if none */
+    const char * inputFileName;  /* '-' if stdin */
+    const char * transparent;  /* NULL if none */
     unsigned int verbose;
 };
 
@@ -19,8 +20,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine ( int argc, char ** argv,
-                   struct cmdlineInfo *cmdlineP ) {
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -31,7 +32,7 @@ parseCommandLine ( int argc, char ** argv,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -40,6 +41,8 @@ parseCommandLine ( int argc, char ** argv,
 
     unsigned int transparentSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
             &cmdlineP->verbose,       0 );
@@ -58,9 +61,9 @@ parseCommandLine ( int argc, char ** argv,
         cmdlineP->transparent = NULL;
 
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else
         pm_error("Too many arguments.  Program takes at most one argument: "
                  "input file name");
@@ -135,31 +138,30 @@ findSameColorRectangle(struct pam *   const pamP,
     mx=0; my=0;
     cnx = pamP->width - col; cny = pamP->height - row;
 
-    for (i=0; (!mx)||(!my); i++) {
+    for (i = 0; !mx || !my; ++i) {
         int j;
-        /*fprintf(stderr,"\n[%d]",i);*/
-        for (j=0; j<=i; j++) {
+        for (j = 0; j <= i; ++j) {
             if (!my) {
-                if (i>=cny) 
-                    my=cny;
-                else
-                    if (((!mx) || (j<mx)) && (j < cnx)) {
-                        /*fprintf(stderr,"%d/%d ",j,i);*/
+                if (i >= cny) 
+                    my = cny;
+                else {
+                    if ((!mx || j < mx) && (j < cnx)) {
                         if (!pnm_tupleequal(pamP, tuples[row+i][col+j],
                                             rectangleColor)) 
                             my = i;
                     }
+                }
             }
             if (!mx) {
-                if (i>=cnx) 
-                    mx=cnx;
-                else
-                    if (((!my) || (j<my)) && (j < cny)) {
-                        /*fprintf(stderr,"%d/%d ",i,j);*/
+                if (i >= cnx) 
+                    mx = cnx;
+                else {
+                    if ((!my || (j < my)) && (j < cny)) {
                         if (!pnm_tupleequal(pamP, tuples[row+j][col+i],
                                             rectangleColor)) 
                             mx = i;
                     }
+                }
             }
         }
     }
@@ -170,7 +172,8 @@ findSameColorRectangle(struct pam *   const pamP,
 
 
 static bool **
-allocOutputtedArray(unsigned int const width, unsigned int const height) {
+allocOutputtedArray(unsigned int const width,
+                    unsigned int const height) {
 
     bool ** outputted;
     unsigned int row;
@@ -180,15 +183,9 @@ allocOutputtedArray(unsigned int const width, unsigned int const height) {
         pm_error("Unable to allocate space for 'outputted' array");
 
     for (row = 0; row < height; ++row) {
-        unsigned int col;
-
         MALLOCARRAY(outputted[row], width);
         if (outputted[row] == NULL)
             pm_error("Unable to allocate space for 'outputted' array");
-
-        for (col = 0; col < width ; ++col)
-          outputted[row][col] = FALSE;
-
     }
     return outputted;
 }
@@ -196,7 +193,8 @@ allocOutputtedArray(unsigned int const width, unsigned int const height) {
 
 
 static void
-freeOutputtedArray(bool ** const outputted, unsigned int const height) {
+freeOutputtedArray(bool **       const outputted,
+                   unsigned int const height) {
 
     unsigned int row;
 
@@ -206,13 +204,40 @@ freeOutputtedArray(bool ** const outputted, unsigned int const height) {
 
 
 
+                       
 static void
-markOutputted(bool ** const outputted,
+markNotOutputted(bool **      const outputted,
+                 unsigned int const upperLeftCol,
+                 unsigned int const upperLeftRow,
+                 unsigned int const width,
+                 unsigned int const height) {
+/*----------------------------------------------------------------------------
+   Mark every pixel in the specified rectangle as not having been output
+   yet.
+-----------------------------------------------------------------------------*/
+    unsigned int const lowerRightCol = upperLeftCol + width;
+    unsigned int const lowerRightRow = upperLeftRow + height;
+    unsigned int row;
+    
+    for (row = upperLeftRow; row < lowerRightRow; ++row) {
+        unsigned int col;
+        for (col = upperLeftCol; col < lowerRightCol; ++col) 
+            outputted[row][col] = FALSE;
+    }
+}
+
+
+
+static void
+markOutputted(bool **      const outputted,
               unsigned int const upperLeftCol,
               unsigned int const upperLeftRow,
               unsigned int const width,
               unsigned int const height) {
-
+/*----------------------------------------------------------------------------
+   Mark every pixel in the specified rectangle as having been output
+   already.
+-----------------------------------------------------------------------------*/
     unsigned int const lowerRightCol = upperLeftCol + width;
     unsigned int const lowerRightRow = upperLeftRow + height;
     unsigned int row;
@@ -232,16 +257,19 @@ main(int argc, char **argv) {
     FILE * ifP;
     struct pam inpam;
     tuple ** tuples;
-    int row;
+    unsigned int row;
     unsigned int rectWidth, rectHeight;
     bool ** outputted;
+        /* Two dimensional array.  outputted[ROW][COL] means the pixel
+           at row ROW, column COL has already been outputted.
+        */
     tuple transparentColor;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -259,8 +287,12 @@ main(int argc, char **argv) {
 
     printf("<TABLE WIDTH=%d HEIGHT=%d BORDER=0 CELLSPACING=0 CELLPADDING=0>\n",
            inpam.width, inpam.height);
+
+    markNotOutputted(outputted, 0, 0, inpam.width, inpam.height);
+        /* No pixel has been outputted yet */
+
     for (row = 0; row < inpam.height; ++row) {
-        int col;
+        unsigned int col;
         printf("<TR>\n");
         pripix(&inpam, tuples[row][0], 1, 1, transparentColor); 
         markOutputted(outputted, 0, row, 1, 1);
@@ -270,7 +302,7 @@ main(int argc, char **argv) {
                 findSameColorRectangle(&inpam, tuples, row, col, 
                                        &rectWidth, &rectHeight);
                 if (cmdline.verbose)
-                    pm_message("[%d/%d] [%d/%d]",
+                    pm_message("[%u/%u] [%u/%u]",
                                col, row, rectWidth, rectHeight);
                 pripix(&inpam, tuples[row][col], rectWidth, rectHeight, 
                        transparentColor);
@@ -286,5 +318,5 @@ main(int argc, char **argv) {
     pnm_freepamarray(tuples, &inpam);
     freeOutputtedArray(outputted, inpam.height);
 
-    exit(0);
+    return 0;
 }
diff --git a/converter/other/pamtompfont.c b/converter/other/pamtompfont.c
new file mode 100644
index 00000000..ba170fef
--- /dev/null
+++ b/converter/other/pamtompfont.c
@@ -0,0 +1,182 @@
+/*----------------------------------------------------------------------------
+                               pamtompfont
+------------------------------------------------------------------------------
+  Part of the Netpbm package.
+
+  Convert a PAM image to an Mplayer bitmap font.
+
+  It is obvious that this format was designed to be an image format and
+  adopted by Mplayer for it's fonts (before Mplayer got the ability to
+  use Freetype to read standard font formats such as TrueType).  But
+  I have no idea what the format was originally.
+
+  In the Mplayer font subset of the format, the image is always grayscale
+  (one byte per pixel) with no palette.
+
+  By Bryan Henderson, San Jose CA 2008.05.18
+
+  Contributed to the public domain by its author.
+-----------------------------------------------------------------------------*/
+
+#include <string.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm.h"
+#include "pam.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilename;
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENTINIT;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilename = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilename = argv[1];
+}
+
+
+
+static void
+validateInput(struct pam * const inpamP) {
+
+    /* The image format does provide for RGB images, but Mplayer doesn't
+       understand that format (and doesn't even recognize it as something
+       it doesn't understand)
+    */
+
+    if (inpamP->depth != 1)
+        pm_error("Input must have depth 1.  This image's depth is %u",
+                 inpamP->depth);
+}
+
+
+
+static void
+writeMpFontHeader(FILE *       const ofP,
+                  struct pam * const inpamP) {
+/*----------------------------------------------------------------------------
+   Write the 32 byte header.
+-----------------------------------------------------------------------------*/
+    fwrite("mhwanh", 1, 6, ofP);  /* Signature */
+
+    fputc(0, ofP);  /* pad */
+    fputc(0, ofP);  /* pad */
+
+    /* Write the old 16 bit width field.  Zero means use the 32 bit one
+       below instead.
+    */
+    pm_writebigshort(ofP, 0);
+
+    /* Height */
+    pm_writebigshort(ofP, inpamP->height);
+
+    /* Number of colors in palette.  Zero means not paletted image */
+    pm_writebigshort(ofP, 0);
+
+    {
+        unsigned int i;
+        for (i = 0; i < 14; ++i)
+            fputc(0, ofP);  /* pad */
+    }
+    /* Width */
+    pm_writebiglong(ofP, inpamP->width);
+}
+
+
+
+static void
+convertRaster(struct pam * const inpamP,
+              FILE *       const ofP) {
+            
+    tuple * tuplerow;
+    unsigned char * outrow;
+    unsigned int row;
+
+    assert(inpamP->depth == 1);
+
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    MALLOCARRAY(outrow, inpamP->width);
+
+    if (outrow == NULL)
+        pm_error("Unable to allocate space for a %u-column output buffer",
+                 inpamP->width);
+
+    for (row = 0; row < inpamP->height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(inpamP, tuplerow);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            outrow[col] =
+                pnm_scalesample(tuplerow[col][0], inpamP->maxval, 255);
+        }
+        
+        fwrite(outrow, 1, inpamP->width, ofP);
+    }
+    free(outrow);
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam inpam;   /* Input PAM image */
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    validateInput(&inpam);
+
+    writeMpFontHeader(stdout, &inpam);
+
+    convertRaster(&inpam, stdout);
+
+    return 0;
+}
diff --git a/converter/other/pamtooctaveimg.c b/converter/other/pamtooctaveimg.c
new file mode 100644
index 00000000..b090281d
--- /dev/null
+++ b/converter/other/pamtooctaveimg.c
@@ -0,0 +1,241 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert a Netpbm file to the GNU Octave image format
+ * by Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright information is at end of file.
+ * ----------------------------------------------------------------------
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+typedef struct {
+    double comp[3];
+        /* comp[0] is red; comp[1] is green; comp[2] is blue */
+} octaveColor;
+
+typedef struct {
+    struct pam pam;
+    unsigned int nColors;
+    tuplehash hash;
+    unsigned int paletteAlloc;
+        /* 'palette' array has this many slots allocated.  Only the first
+           'nColors' are meaningful.
+        */
+    octaveColor * palette;
+    double normalizer;
+        /* 1/maxval */
+} cmap;
+
+
+
+static void
+initCmap(cmap * const cmapP,
+         sample const maxval) {
+
+    cmapP->pam.size             = sizeof(cmapP->pam.size);
+    cmapP->pam.len              = PAM_STRUCT_SIZE(tuple_type);
+    cmapP->pam.depth            = 3;
+    cmapP->pam.maxval           = maxval;
+    cmapP->pam.bytes_per_sample = pnm_bytespersample(maxval);
+
+    cmapP->normalizer   = 1.0/maxval;
+    cmapP->nColors      = 0;
+    cmapP->paletteAlloc = 0;
+    cmapP->palette      = NULL;
+    cmapP->hash         = pnm_createtuplehash();
+}
+
+
+
+static void
+termCmap(cmap * const cmapP) {
+    pnm_destroytuplehash(cmapP->hash);
+
+    free(cmapP->palette);
+}
+
+
+
+static void
+findOrAddColor(tuple          const color,
+               cmap *         const cmapP,
+               unsigned int * const colorIndexP) {
+/*----------------------------------------------------------------------------
+  Return as *colorIndexP the colormap index of color 'color' in
+  colormap *cmapP.  If the color isn't in the map, give it a new
+  colormap index, put it in the colormap, and return that.
+-----------------------------------------------------------------------------*/
+    bool found;
+    int colorIndex;
+
+    pnm_lookuptuple(&cmapP->pam, cmapP->hash, color, &found, &colorIndex);
+
+    if (!found) {
+        bool fits;
+        unsigned int plane;
+
+        colorIndex = cmapP->nColors++;
+
+        if (cmapP->nColors > cmapP->paletteAlloc) {
+            cmapP->paletteAlloc *= 2;
+            REALLOCARRAY(cmapP->palette, cmapP->nColors);
+        }
+        for (plane = 0; plane < 3; ++plane)
+            cmapP->palette[colorIndex].comp[plane] =
+                color[plane] * cmapP->normalizer;
+
+        pnm_addtotuplehash(&cmapP->pam, cmapP->hash, color, colorIndex, &fits);
+
+        if (!fits)
+            pm_error("Out of memory constructing color map, on %uth color",
+                     cmapP->nColors);
+    }
+    *colorIndexP = colorIndex;
+}
+
+
+
+static void
+outputColormap(FILE * const ofP,
+               cmap   const cmap) {
+/*----------------------------------------------------------------------------
+  Output the colormap as a GNU Octave matrix.
+-----------------------------------------------------------------------------*/
+    unsigned int colorIndex;
+
+    fprintf(ofP, "# name: map\n");
+    fprintf(ofP, "# type: matrix\n");
+    fprintf(ofP, "# rows: %u\n", cmap.nColors);
+    fprintf(ofP, "# columns: 3\n");
+
+    for (colorIndex = 0; colorIndex < cmap.nColors; ++colorIndex) {
+        unsigned int plane;
+
+        assert(cmap.pam.depth == 3);
+
+        for (plane = 0; plane < 3; ++plane)
+            fprintf(ofP, " %.10f", cmap.palette[colorIndex].comp[plane]);
+
+        fprintf(ofP, "\n");
+    }
+}
+
+
+
+static void
+convertToOctave(FILE * const ifP,
+                FILE * const ofP) {
+
+    struct pam inpam;
+    tuple * inRow;
+    unsigned int row;
+    cmap cmap;
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pnm_setminallocationdepth(&inpam, 3);
+    
+    /* Output the image as a GNU Octave matrix.  For each row of the
+     * input file we immediately output indexes into the colormap then,
+     * when we're finished, we output the colormap as a second
+     * matrix. */
+    fprintf(ofP, "# name: img\n");
+    fprintf(ofP, "# type: matrix\n");
+    fprintf(ofP, "# rows: %u\n", inpam.height);
+    fprintf(ofP, "# columns: %u\n", inpam.width);
+
+    initCmap(&cmap, inpam.maxval);
+
+    inRow = pnm_allocpamrow(&inpam);
+    for (row = 0; row < inpam.height; ++row) {
+        unsigned int col;
+        pnm_readpamrow(&inpam, inRow);
+
+        pnm_makerowrgb(&inpam, inRow);
+
+        for (col = 0; col < inpam.width; ++col) {
+            unsigned int colorIndex;
+            findOrAddColor(inRow[col], &cmap, &colorIndex);
+            fprintf(ofP, " %u", colorIndex + 1);
+        }
+        fprintf(ofP, "\n");
+    }
+    pm_message("%u colors in palette", cmap.nColors);
+
+    pnm_freepamrow(inRow);
+    outputColormap(ofP, cmap);
+
+    termCmap(&cmap);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE * ifP;
+    const char * inputName;
+
+    pnm_init(&argc, argv);
+
+    inputName = argc-1 > 0 ? argv[1] : "-";
+
+    ifP = pm_openr(inputName);
+    
+    if (streq(inputName, "-"))
+        fprintf(stdout, "# Created by pamtooctave\n");
+    else
+        fprintf(stdout, "# Created from '%s' by pamtooctave\n", inputName);
+
+    convertToOctave(ifP, stdout);
+    
+    pm_close(ifP);
+
+    return 0;
+}
+
+
+
+/*
+ * Copyright (C) 2007 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ----------------------------------------------------------------------
+ */
diff --git a/converter/other/pamtopam.c b/converter/other/pamtopam.c
new file mode 100644
index 00000000..cae54060
--- /dev/null
+++ b/converter/other/pamtopam.c
@@ -0,0 +1,57 @@
+/*=============================================================================
+                               pamtopam
+===============================================================================
+  Part of the Netpbm package.
+
+  Copy PAM and PNM (i.e. PBM, PGM, or PPM) images from Standard Input
+  to Standard Output (while converting PNM images to PAM)
+
+  By Paul Bolle October 2007.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+
+#include "pm_c_util.h"
+#include "pam.h"
+
+int
+main(int argc, const char * argv[]) {
+
+    bool       eof;     /* no more images in input stream */
+    struct pam inpam;   /* Input PAM image */
+    struct pam outpam;  /* Output PAM image */
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 != 0)
+        pm_error("Program takes no arguments.  Input is from Standard Input");
+
+    eof = FALSE;
+    while (!eof) {
+        pnm_readpaminit(stdin, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+        outpam = inpam;
+        outpam.file = stdout;
+        outpam.format = PAM_FORMAT;
+
+        pnm_writepaminit(&outpam);
+
+        {
+            tuple * tuplerow;
+
+            tuplerow = pnm_allocpamrow(&inpam);
+            {
+                unsigned int row;
+
+                for (row = 0; row < inpam.height; ++row) {
+                    pnm_readpamrow(&inpam, tuplerow);
+                    pnm_writepamrow(&outpam, tuplerow);
+                }
+            }
+            pnm_freepamrow(tuplerow);
+        }
+        pnm_nextimage(stdin, &eof);
+    }
+
+    return 0;
+}
diff --git a/converter/other/pamtopfm.c b/converter/other/pamtopfm.c
index ee44eeb5..129b8eee 100644
--- a/converter/other/pamtopfm.c
+++ b/converter/other/pamtopfm.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pm_gamma.h"
 #include "shhopt.h"
diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c
index cc1164da..86f6514c 100644
--- a/converter/other/pamtopnm.c
+++ b/converter/other/pamtopnm.c
@@ -12,6 +12,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -108,6 +109,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE* ifP;
+    bool eof;   /* no more images in input stream */
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PNM image */
 
@@ -117,39 +119,45 @@ main(int argc, char *argv[]) {
 
     ifP = pm_openr(cmdline.inputFilespec);
 
-    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+    eof = FALSE;
+    while (!eof) {
+        pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
-    validateTupleType(inpam, cmdline.assume);
+        validateTupleType(inpam, cmdline.assume);
 
-    outpam = inpam;
-    outpam.file = stdout;
-    
-    if (inpam.depth < 3) {
-        outpam.depth = 1;
-        if (inpam.maxval == 1)
-            outpam.format = PBM_FORMAT;
-        else 
-            outpam.format = PGM_FORMAT;
-    } else {
-        outpam.depth = 3;
-        outpam.format = PPM_FORMAT;
-    }
+        outpam = inpam;
+        outpam.file = stdout;
+        
+        if (inpam.depth < 3) {
+            outpam.depth = 1;
+            if (inpam.maxval == 1)
+                outpam.format = PBM_FORMAT;
+            else 
+                outpam.format = PGM_FORMAT;
+        } else {
+            outpam.depth = 3;
+            outpam.format = PPM_FORMAT;
+        }
 
-    pnm_writepaminit(&outpam);
+        pnm_writepaminit(&outpam);
 
-    {
-        tuple *tuplerow;
-        
-        tuplerow = pnm_allocpamrow(&inpam);      
-        { 
-            int row;
+        {
+            tuple *tuplerow;
             
-            for (row = 0; row < inpam.height; row++) {
-                pnm_readpamrow(&inpam, tuplerow);
-                pnm_writepamrow(&outpam, tuplerow);
+            tuplerow = pnm_allocpamrow(&inpam);      
+            { 
+                int row;
+                
+                for (row = 0; row < inpam.height; row++) {
+                    pnm_readpamrow(&inpam, tuplerow);
+                    pnm_writepamrow(&outpam, tuplerow);
+                }
             }
+            pnm_freepamrow(tuplerow);        
         }
-        pnm_freepamrow(tuplerow);        
+
+        pnm_nextimage(ifP, &eof);
     }
+
     return 0;
 }
diff --git a/converter/other/pamtosvg/Makefile b/converter/other/pamtosvg/Makefile
index ba03fd68..8b033020 100644
--- a/converter/other/pamtosvg/Makefile
+++ b/converter/other/pamtosvg/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other/pamtosvg
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 BINARIES = pamtosvg
 
@@ -47,7 +47,7 @@ MERGEBINARIES = $(BINARIES)
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 pamtosvg: $(PAMTOSVG_OBJECTS) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $(PAMTOSVG_OBJECTS) \
diff --git a/converter/other/pamtosvg/curve.c b/converter/other/pamtosvg/curve.c
index cc8aeb59..369a0cd3 100644
--- a/converter/other/pamtosvg/curve.c
+++ b/converter/other/pamtosvg/curve.c
@@ -42,18 +42,19 @@ int_to_real_coord(pm_pixelcoord const int_coord) {
 
 /* Return an entirely empty curve.  */
 
-curve_type
-new_curve (void)
-{
-  curve_type curve;
-  MALLOCVAR_NOFAIL(curve);
-  curve->point_list = NULL;
-  CURVE_LENGTH (curve) = 0;
-  CURVE_CYCLIC (curve) = false;
-  CURVE_START_TANGENT (curve) = CURVE_END_TANGENT (curve) = NULL;
-  PREVIOUS_CURVE (curve) = NEXT_CURVE (curve) = NULL;
+curve *
+new_curve(void) {
+  curve * curveP;
 
-  return curve;
+  MALLOCVAR_NOFAIL(curveP);
+
+  curveP->point_list = NULL;
+  CURVE_LENGTH(curveP) = 0;
+  CURVE_CYCLIC(curveP) = false;
+  PREVIOUS_CURVE(curveP)  = NULL;
+  NEXT_CURVE(curveP)      = NULL;
+
+  return curveP;
 }
 
 
@@ -71,23 +72,39 @@ copy_most_of_curve (curve_type old_curve)
   return curve;
 }
 
+void
+move_curve(curve * const dstP,
+           curve * const srcP) {
+
+    /* Move ownership of dynamically allocated memory from source 
+       to destination; destroy source.
+    */
+
+   if (CURVE_LENGTH(dstP) > 0)
+       free(dstP->point_list);
+    
+   *dstP = *srcP;
+
+   free(srcP);
+}
+
+
 
 /* The length of CURVE will be zero if we ended up not being able to fit
    it (which in turn implies a problem elsewhere in the program, but at
    any rate, we shouldn't try here to free the nonexistent curve).  */
 
 void
-free_curve (curve_type curve)
-{
-  if (CURVE_LENGTH (curve) > 0)
-    free (curve->point_list);
-  if (CURVE_START_TANGENT (curve))
-    free (CURVE_START_TANGENT (curve));
-  if (CURVE_END_TANGENT (curve))
-    free (CURVE_END_TANGENT (curve));
+free_curve(curve * const curveP) {
+
+   if (CURVE_LENGTH(curveP) > 0)
+       free(curveP->point_list);
+
+   free(curveP);
 }
 
 
+
 void
 append_point(curve_type  const curve,
              float_coord const coord) {
@@ -123,99 +140,84 @@ append_pixel(curve_type    const curve,
     }									\
   while (0)
 
+
+
 void
-log_curve (curve_type curve, bool print_t)
-{
-  unsigned this_point;
-
-  if (!log_file) return;
-
-  LOG1 ("curve id = %lx:\n", (unsigned long) curve);
-  LOG1 ("  length = %u.\n", CURVE_LENGTH (curve));
-  if (CURVE_CYCLIC (curve))
-    LOG ("  cyclic.\n");
-
-  /* It should suffice to check just one of the tangents for being null
-     -- either they both should be, or neither should be.  */
-  if (CURVE_START_TANGENT (curve) != NULL)
-    LOG4 ("  tangents = (%.3f,%.3f) & (%.3f,%.3f).\n",
-          CURVE_START_TANGENT (curve)->dx, CURVE_START_TANGENT (curve)->dy,
-          CURVE_END_TANGENT (curve)->dx, CURVE_END_TANGENT (curve)->dy);
-
-  LOG ("  ");
-
-  /* If the curve is short enough, don't use ellipses.  */
-  if (CURVE_LENGTH (curve) <= NUM_TO_PRINT * 2)
-    {
-      for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
-        {
-          LOG_CURVE_POINT (curve, this_point, print_t);
-          LOG (" ");
-
-          if (this_point != CURVE_LENGTH (curve) - 1
-              && (this_point + 1) % NUM_TO_PRINT == 0)
-            LOG ("\n  ");
+log_curve(curve * const curveP,
+          bool    const print_t) {
+
+    if (!log_file)
+        return;
+
+    LOG1("curve id = %lx:\n", (unsigned long) curveP);
+    LOG1("  length = %u.\n", CURVE_LENGTH(curveP));
+    if (CURVE_CYCLIC(curveP))
+        LOG("  cyclic.\n");
+
+    LOG("  ");
+
+    /* If the curve is short enough, don't use ellipses.  */
+    if (CURVE_LENGTH(curveP) <= NUM_TO_PRINT * 2) {
+        unsigned int thisPoint;
+    
+        for (thisPoint = 0; thisPoint < CURVE_LENGTH(curveP); ++thisPoint) {
+            LOG_CURVE_POINT(curveP, thisPoint, print_t);
+            LOG(" ");
+
+            if (thisPoint != CURVE_LENGTH(curveP) - 1
+                && (thisPoint + 1) % NUM_TO_PRINT == 0)
+                LOG("\n  ");
         }
-    }
-  else
-    {
-      for (this_point = 0;
-           this_point < NUM_TO_PRINT && this_point < CURVE_LENGTH (curve);
-           this_point++)
-        {
-          LOG_CURVE_POINT (curve, this_point, print_t);
-          LOG (" ");
+    } else {
+        unsigned int thisPoint;
+        for (thisPoint = 0;
+             thisPoint < NUM_TO_PRINT && thisPoint < CURVE_LENGTH(curveP);
+             ++thisPoint) {
+            LOG_CURVE_POINT(curveP, thisPoint, print_t);
+            LOG(" ");
         }
 
-      LOG ("...\n   ...");
+        LOG("...\n   ...");
 
-      for (this_point = CURVE_LENGTH (curve) - NUM_TO_PRINT;
-           this_point < CURVE_LENGTH (curve);
-           this_point++)
-        {
-          LOG (" ");
-          LOG_CURVE_POINT (curve, this_point, print_t);
+        for (thisPoint = CURVE_LENGTH(curveP) - NUM_TO_PRINT;
+             thisPoint < CURVE_LENGTH(curveP);
+             ++thisPoint) {
+            LOG(" ");
+            LOG_CURVE_POINT(curveP, thisPoint, print_t);
         }
     }
-
-  LOG (".\n");
+    LOG(".\n");
 }
 
 
 /* Like `log_curve', but write the whole thing.  */
 
 void
-log_entire_curve (curve_type curve)
-{
-  unsigned this_point;
+log_entire_curve(curve * const curveP) {
 
-  if (!log_file) return;
+    unsigned int thisPoint;
 
-  LOG1 ("curve id = %lx:\n", (unsigned long) curve);
-  LOG1 ("  length = %u.\n", CURVE_LENGTH (curve));
-  if (CURVE_CYCLIC (curve))
-    LOG ("  cyclic.\n");
+    if (!log_file)
+        return;
 
-  /* It should suffice to check just one of the tangents for being null
-     -- either they both should be, or neither should be.  */
-  if (CURVE_START_TANGENT (curve) != NULL)
-    LOG4 ("  tangents = (%.3f,%.3f) & (%.3f,%.3f).\n",
-          CURVE_START_TANGENT (curve)->dx, CURVE_START_TANGENT (curve)->dy,
-          CURVE_END_TANGENT (curve)->dx, CURVE_END_TANGENT (curve)->dy);
+    LOG1("curve id = %lx:\n", (unsigned long) curveP);
+    LOG1("  length = %u.\n", CURVE_LENGTH(curveP));
+    if (CURVE_CYCLIC(curveP))
+        LOG("  cyclic.\n");
 
-  LOG (" ");
+    LOG(" ");
 
-  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
-    {
-      LOG (" ");
-      LOG_CURVE_POINT (curve, this_point, true);
-      /* Compiler warning `Condition is always true' can be ignored */
+    for (thisPoint = 0; thisPoint < CURVE_LENGTH(curveP); ++thisPoint) {
+        LOG(" ");
+        LOG_CURVE_POINT(curveP, thisPoint, true);
+        /* Compiler warning `Condition is always true' can be ignored */
     }
 
-  LOG (".\n");
+    LOG(".\n");
 }
 
 
+
 /* Return an initialized but empty curve list.  */
 
 curve_list_type
@@ -233,19 +235,16 @@ new_curve_list (void)
 /* Free a curve list and all the curves it contains.  */
 
 void
-free_curve_list(curve_list_type * const curve_list) {
+free_curve_list(curve_list_type * const curveListP) {
 
-  unsigned this_curve;
+    unsigned int thisCurve;
 
-  for (this_curve = 0; this_curve < curve_list->length; this_curve++)
-    {
-      free_curve (curve_list->data[this_curve]);
-      free (curve_list->data[this_curve]);
-    }
+    for (thisCurve = 0; thisCurve < curveListP->length; ++thisCurve)
+        free_curve(curveListP->data[thisCurve]);
 
-  /* If the character was empty, it won't have any curves.  */
-  if (curve_list->data != NULL)
-    free (curve_list->data);
+    /* If the character was empty, it won't have any curves.  */
+    if (curveListP->data != NULL)
+        free (curveListP->data);
 }
 
 
diff --git a/converter/other/pamtosvg/curve.h b/converter/other/pamtosvg/curve.h
index ee046620..ba5f1833 100644
--- a/converter/other/pamtosvg/curve.h
+++ b/converter/other/pamtosvg/curve.h
@@ -20,19 +20,27 @@ typedef struct {
 
 
 
-struct curve {
+typedef struct curve {
 /*----------------------------------------------------------------------------
   An ordered list of contiguous points in the raster, with no corners
   in it.  I.e. something that could reasonably be fit to a spline.
 -----------------------------------------------------------------------------*/
     point_type *   point_list;
+        /* Array of the points in the curve.  Malloc'ed.  Size is 'length'.
+           if 'length' is zero, this is meaningless and no memory is
+           allocated.
+        */
     unsigned       length;
+        /* Number of points in the curve */
     bool           cyclic;
-    vector_type *  start_tangent;
-    vector_type *  end_tangent;
+
+    /* 'previous' and 'next' links are for the doubly linked list which is
+       a chain of all curves in an outline.  The chain is a cycle for a
+       closed outline and linear for an open outline.
+    */
     struct curve * previous;
     struct curve * next;
-};
+} curve;
 
 typedef struct curve * curve_type;
 
@@ -61,9 +69,6 @@ typedef struct curve * curve_type;
   ? CURVE_CYCLIC (c) ? (signed int) CURVE_LENGTH (c) + (signed int) (n) - 1 : -1\
   : (signed int) (n) - 1)
 
-/* The tangents at the endpoints are computed using the neighboring curves.  */
-#define CURVE_START_TANGENT(c) ((c)->start_tangent)
-#define CURVE_END_TANGENT(c) ((c)->end_tangent)
 #define PREVIOUS_CURVE(c) ((c)->previous)
 #define NEXT_CURVE(c) ((c)->next)
 
@@ -74,17 +79,23 @@ extern curve_type new_curve (void);
 /* Return a curve the same as C, except without any points.  */
 extern curve_type copy_most_of_curve (curve_type c);
 
-/* Free the memory C uses.  */
-extern void free_curve (curve_type c);
+void
+move_curve(curve * const dstP,
+           curve * const srcP);
+
+void
+free_curve(curve * const curveP);
+
+/* Like `append_pixel', for a point in real coordinates.  */
+void
+append_point(curve_type  const curve,
+             float_coord const coord);
 
 /* Append the point P to the end of C's list.  */
 void
 append_pixel(curve_type    const c,
              pm_pixelcoord const p);
 
-/* Like `append_pixel', for a point in real coordinates.  */
-extern void append_point (curve_type const c, float_coord const p);
-
 /* Write some or all, respectively, of the curve C in human-readable
    form to the log file, if logging is enabled.  */
 extern void log_curve (curve_type c, bool print_t);
@@ -99,11 +110,12 @@ typedef struct {
 /*----------------------------------------------------------------------------
    An ordered list of contiguous curves of a particular color.
 -----------------------------------------------------------------------------*/
-    curve_type * data;
-    unsigned     length;
-    bool         clockwise;
-    pixel        color;
-    bool         open;
+    curve ** data;
+        /* data[i] is the handle of the ith curve in the list */
+    unsigned length;
+    bool     clockwise;
+    pixel    color;
+    bool     open;
         /* The curve list does not form a closed shape;  i.e. the last
            curve doesn't end where the first one starts.
         */
diff --git a/converter/other/pamtosvg/fit.c b/converter/other/pamtosvg/fit.c
index 6bc2fe88..5ba7a2f6 100644
--- a/converter/other/pamtosvg/fit.c
+++ b/converter/other/pamtosvg/fit.c
@@ -54,21 +54,7 @@ typedef struct index_list
 #define INDEX_LIST_LENGTH(i_l)  ((i_l).length)
 #define GET_LAST_INDEX(i_l)  ((i_l).data[INDEX_LIST_LENGTH (i_l) - 1])
 
-static void append_index (index_list_type *, unsigned);
-static void free_index_list (index_list_type *);
-static index_list_type new_index_list (void);
-static void remove_adjacent_corners (index_list_type *, unsigned, bool,
-                     at_exception_type * exception);
-static void filter (curve_type, fitting_opts_type *);
-static void find_vectors
-  (unsigned const, pixel_outline_type const, vector_type * const, vector_type * const, unsigned const);
-static float find_error (curve_type, spline_type, unsigned *,
-               at_exception_type * exception);
-static vector_type find_half_tangent (curve_type, bool start, unsigned *, unsigned);
-static void find_tangent (curve_type, bool, bool, unsigned);
-static void remove_knee_points (curve_type const, bool const);
-static void set_initial_parameter_values (curve_type);
-static float distance (float_coord, float_coord);
+
 
 
 static pm_pixelcoord
@@ -86,6 +72,50 @@ real_to_int_coord(float_coord const real_coord) {
 }
 
 
+/* Lists of array indices (well, that is what we use it for).  */
+
+static index_list_type
+new_index_list (void)
+{
+  index_list_type index_list;
+
+  index_list.data = NULL;
+  INDEX_LIST_LENGTH (index_list) = 0;
+
+  return index_list;
+}
+
+static void
+free_index_list (index_list_type *index_list)
+{
+  if (INDEX_LIST_LENGTH (*index_list) > 0)
+    {
+      free (index_list->data);
+      index_list->data = NULL;
+      INDEX_LIST_LENGTH (*index_list) = 0;
+    }
+}
+
+static void
+append_index (index_list_type *list, unsigned new_index)
+{
+  INDEX_LIST_LENGTH (*list)++;
+  REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list));
+  list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index;
+}
+
+
+/* Return the Euclidean distance between P1 and P2.  */
+
+static float
+distance (float_coord p1, float_coord p2)
+{
+  float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z;
+  return (float) sqrt (SQR(x) + SQR(y) + SQR(z));
+}
+
+
+
 static void
 appendCorner(index_list_type *  const cornerListP,
              unsigned int       const pixelSeq,
@@ -102,6 +132,45 @@ appendCorner(index_list_type *  const cornerListP,
 
 
 static void
+find_vectors(unsigned int       const test_index,
+             pixel_outline_type const outline,
+             vector_type *      const in,
+             vector_type *      const out,
+             unsigned int       const corner_surround) {
+/*----------------------------------------------------------------------------
+  Return the difference vectors coming in and going out of the outline
+  OUTLINE at the point whose index is TEST_INDEX.  In Phoenix,
+  Schneider looks at a single point on either side of the point we're
+  considering.  That works for him because his points are not touching.
+  But our points *are* touching, and so we have to look at
+  `corner_surround' points on either side, to get a better picture of
+  the outline's shape.
+-----------------------------------------------------------------------------*/
+    int i;
+    unsigned n_done;
+    pm_pixelcoord const candidate = O_COORDINATE(outline, test_index);
+
+    in->dx  = in->dy  = in->dz  = 0.0;
+    out->dx = out->dy = out->dz = 0.0;
+    
+    /* Add up the differences from p of the `corner_surround' points
+       before p.
+    */
+    for (i = O_PREV(outline, test_index), n_done = 0;
+         n_done < corner_surround;
+         i = O_PREV(outline, i), ++n_done)
+        *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate));
+    
+    /* And the points after p. */
+    for (i = O_NEXT (outline, test_index), n_done = 0;
+         n_done < corner_surround;
+         i = O_NEXT(outline, i), ++n_done)
+        *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate));
+}
+
+
+
+static void
 lookAheadForBetterCorner(pixel_outline_type  const outline,
                          unsigned int        const basePixelSeq,
                          float               const baseCornerAngle,
@@ -210,6 +279,283 @@ establishCornerSearchLimits(pixel_outline_type  const outline,
 
 
 static void
+remove_adjacent_corners(index_list_type *   const list,
+                        unsigned int        const last_index,
+                        bool                const remove_adj_corners,
+                        at_exception_type * const exception) {
+/*----------------------------------------------------------------------------
+   Remove adjacent points from the index list LIST.  We do this by first
+   sorting the list and then running through it.  Since these lists are
+   quite short, a straight selection sort (e.g., p.139 of the Art of
+   Computer Programming, vol.3) is good enough.  LAST_INDEX is the index
+   of the last pixel on the outline, i.e., the next one is the first
+   pixel. We need this for checking the adjacency of the last corner.
+
+   We need to do this because the adjacent corners turn into
+   two-pixel-long curves, which can be fit only by straight lines.
+-----------------------------------------------------------------------------*/
+  unsigned int j;
+  unsigned int last;
+  index_list_type new_list = new_index_list ();
+
+  for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--)
+    {
+      unsigned search;
+      unsigned temp;
+      /* Find maximal element below `j'.  */
+      unsigned max_index = j;
+
+      for (search = 0; search < j; search++)
+        if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index))
+          max_index = search;
+
+      if (max_index != j)
+        {
+          temp = GET_INDEX (*list, j);
+          GET_INDEX (*list, j) = GET_INDEX (*list, max_index);
+          GET_INDEX (*list, max_index) = temp;
+        }
+    }
+
+  /* The list is sorted.  Now look for adjacent entries.  Each time
+     through the loop we insert the current entry and, if appropriate,
+     the next entry.  */
+  for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++)
+    {
+      unsigned current = GET_INDEX (*list, j);
+      unsigned next = GET_INDEX (*list, j + 1);
+
+      /* We should never have inserted the same element twice.  */
+      /* assert (current != next); */
+
+      if ((remove_adj_corners) && ((next == current + 1) || (next == current)))
+        j++;
+
+      append_index (&new_list, current);
+    }
+
+  /* Don't append the last element if it is 1) adjacent to the previous
+     one; or 2) adjacent to the very first one.  */
+  last = GET_LAST_INDEX (*list);
+  if (INDEX_LIST_LENGTH (new_list) == 0
+      || !(last == GET_LAST_INDEX (new_list) + 1
+           || (last == last_index && GET_INDEX (*list, 0) == 0)))
+    append_index (&new_list, last);
+
+  free_index_list (list);
+  *list = new_list;
+}
+
+/* A ``knee'' is a point which forms a ``right angle'' with its
+   predecessor and successor.  See the documentation (the `Removing
+   knees' section) for an example and more details.
+
+   The argument CLOCKWISE tells us which direction we're moving.  (We
+   can't figure that information out from just the single segment with
+   which we are given to work.)
+
+   We should never find two consecutive knees.
+
+   Since the first and last points are corners (unless the curve is
+   cyclic), it doesn't make sense to remove those.
+*/
+
+/* This evaluates to true if the vector V is zero in one direction and
+   nonzero in the other.  */
+#define ONLY_ONE_ZERO(v)                                                \
+  (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0))
+
+/* There are four possible cases for knees, one for each of the four
+   corners of a rectangle; and then the cases differ depending on which
+   direction we are going around the curve.  The tests are listed here
+   in the order of upper left, upper right, lower right, lower left.
+   Perhaps there is some simple pattern to the
+   clockwise/counterclockwise differences, but I don't see one.  */
+#define CLOCKWISE_KNEE(prev_delta, next_delta)                                                  \
+  ((prev_delta.dx == -1.0 && next_delta.dy == 1.0)                                              \
+   || (prev_delta.dy == 1.0 && next_delta.dx == 1.0)                                    \
+   || (prev_delta.dx == 1.0 && next_delta.dy == -1.0)                                   \
+   || (prev_delta.dy == -1.0 && next_delta.dx == -1.0))
+
+#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)                                   \
+  ((prev_delta.dy == 1.0 && next_delta.dx == -1.0)                                              \
+   || (prev_delta.dx == 1.0 && next_delta.dy == 1.0)                                    \
+   || (prev_delta.dy == -1.0 && next_delta.dx == 1.0)                                   \
+   || (prev_delta.dx == -1.0 && next_delta.dy == -1.0))
+
+
+
+static void
+remove_knee_points(curve * const curveP,
+                   bool    const clockwise) {
+
+    unsigned int const offset = CURVE_CYCLIC(curveP) ? 0 : 1;
+    curve * const trimmedCurveP = copy_most_of_curve(curveP);
+
+    pm_pixelcoord previous;
+    unsigned int i;
+
+    if (!CURVE_CYCLIC(curveP))
+        append_pixel(trimmedCurveP,
+                     real_to_int_coord(CURVE_POINT(curveP, 0)));
+
+    previous = real_to_int_coord(CURVE_POINT(curveP,
+                                             CURVE_PREV(curveP, offset)));
+
+    for (i = offset; i < CURVE_LENGTH(curveP) - offset; ++i) {
+        pm_pixelcoord const current =
+            real_to_int_coord(CURVE_POINT(curveP, i));
+        pm_pixelcoord const next =
+            real_to_int_coord(CURVE_POINT(curveP, CURVE_NEXT(curveP, i)));
+        vector_type const prev_delta = IPsubtract(previous, current);
+        vector_type const next_delta = IPsubtract(next, current);
+
+        if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta)
+            && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta))
+                || (!clockwise
+                    && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta))))
+            LOG2(" (%d,%d)", current.col, current.row);
+        else {
+            previous = current;
+            append_pixel(trimmedCurveP, current);
+        }
+    }
+
+    if (!CURVE_CYCLIC(curveP))
+        append_pixel(trimmedCurveP,
+                     real_to_int_coord(LAST_CURVE_POINT(curveP)));
+
+    if (CURVE_LENGTH(trimmedCurveP) == CURVE_LENGTH(curveP))
+        LOG(" (none)");
+
+    LOG(".\n");
+
+    move_curve(curveP, trimmedCurveP);
+}
+
+
+
+static void
+filter(curve *             const curveP,
+       fitting_opts_type * const fittingOptsP) {
+/*----------------------------------------------------------------------------
+  Smooth the curve by adding in neighboring points.  Do this
+  fittingOptsP->filter_iterations times.  But don't change the corners.
+-----------------------------------------------------------------------------*/
+    unsigned int const offset = CURVE_CYCLIC(curveP) ? 0 : 1;
+
+    unsigned int iteration, thisPoint;
+    float_coord prevNewPoint;
+    
+    /* We must have at least three points -- the previous one, the current
+       one, and the next one.  But if we don't have at least five, we will
+       probably collapse the curve down onto a single point, which means
+       we won't be able to fit it with a spline.
+    */
+    if (CURVE_LENGTH(curveP) < 5) {
+        LOG1("Length is %u, not enough to filter.\n", CURVE_LENGTH(curveP));
+        return;
+    }
+
+    prevNewPoint.x = FLT_MAX;
+    prevNewPoint.y = FLT_MAX;
+    prevNewPoint.z = FLT_MAX;
+    
+    for (iteration = 0;
+         iteration < fittingOptsP->filter_iterations;
+         ++iteration) {
+        curve * const newcurveP = copy_most_of_curve(curveP);
+
+        bool collapsed;
+        
+        collapsed = false;  /* initial value */
+
+        /* Keep the first point on the curve.  */
+        if (offset)
+            append_point(newcurveP, CURVE_POINT(curveP, 0));
+        
+        for (thisPoint = offset;
+             thisPoint < CURVE_LENGTH(curveP) - offset;
+             ++thisPoint) {
+            vector_type in, out, sum;
+            float_coord newPoint;
+            
+            /* Calculate the vectors in and out, computed by looking
+               at n points on either side of this_point.  Experimental
+               it was found that 2 is optimal.
+            */
+
+            signed int prev, prevprev; /* have to be signed */
+            unsigned int next, nextnext;
+            float_coord candidate = CURVE_POINT(curveP, thisPoint);
+            
+            prev = CURVE_PREV(curveP, thisPoint);
+            prevprev = CURVE_PREV(curveP, prev);
+            next = CURVE_NEXT(curveP, thisPoint);
+            nextnext = CURVE_NEXT(curveP, next);
+            
+            /* Add up the differences from p of the `surround' points
+               before p.
+            */
+            in.dx = in.dy = in.dz = 0.0;
+
+            in = Vadd(in, Psubtract(CURVE_POINT(curveP, prev), candidate));
+            if (prevprev >= 0)
+                in = Vadd(in,
+                          Psubtract(CURVE_POINT(curveP, prevprev), candidate));
+            
+            /* And the points after p.  Don't use more points after p than we
+               ended up with before it.
+            */
+            out.dx = out.dy = out.dz = 0.0;
+            
+            out = Vadd(out, Psubtract(CURVE_POINT(curveP, next), candidate));
+            if (nextnext < CURVE_LENGTH(curveP))
+                out = Vadd(out,
+                           Psubtract(CURVE_POINT(curveP, nextnext),
+                                     candidate));
+            
+            /* Start with the old point.  */
+            newPoint = candidate;
+            sum = Vadd(in, out);
+            /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */
+            newPoint.x += sum.dx / 6;
+            newPoint.y += sum.dy / 6;
+            newPoint.z += sum.dz / 6;
+            if (fabs(prevNewPoint.x - newPoint.x) < 0.3
+                && fabs (prevNewPoint.y - newPoint.y) < 0.3
+                && fabs (prevNewPoint.z - newPoint.z) < 0.3) {
+                collapsed = true;
+                break;
+            }
+            
+            /* Put the newly computed point into a separate curve, so it
+               doesn't affect future computation (on this iteration).
+            */
+            append_point(newcurveP, prevNewPoint = newPoint);
+        }
+        
+        if (collapsed)
+            free_curve(newcurveP);
+        else {
+            /* Just as with the first point, we have to keep the last
+               point.
+            */
+            if (offset)
+                append_point(newcurveP, LAST_CURVE_POINT(curveP));
+            
+            /* Set the original curve to the newly filtered one, and go
+               again.
+            */
+            move_curve(curveP, newcurveP);
+        }
+    }
+    log_curve(curveP, false);
+}
+
+
+
+static void
 removeAdjacent(index_list_type *   const cornerListP,
                pixel_outline_type  const outline,
                fitting_opts_type * const fittingOptsP,
@@ -340,25 +686,27 @@ makeOutlineOneCurve(pixel_outline_type const outline,
                     curve_list_type *  const curveListP) {
 /*----------------------------------------------------------------------------
    Add to *curveListP a single curve that represents the outline 'outline'.
+
+   That curve does not have beginning and ending slope information.
 -----------------------------------------------------------------------------*/
-    curve_type curve;
+    curve * curveP;
     unsigned int pixelSeq;
 
-    curve = new_curve();
-    
+    curveP = new_curve();
+
     for (pixelSeq = 0; pixelSeq < O_LENGTH(outline); ++pixelSeq)
-        append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+        append_pixel(curveP, O_COORDINATE(outline, pixelSeq));
     
-    if (curveListP->open)
-        CURVE_CYCLIC(curve) = false;
+    if (outline.open)
+        CURVE_CYCLIC(curveP) = false;
     else
-        CURVE_CYCLIC(curve) = true;
+        CURVE_CYCLIC(curveP) = true;
     
     /* Make it a one-curve cycle */
-    NEXT_CURVE(curve)     = curve;
-    PREVIOUS_CURVE(curve) = curve;
+    NEXT_CURVE(curveP)     = curveP;
+    PREVIOUS_CURVE(curveP) = curveP;
 
-    append_curve(curveListP, curve);
+    append_curve(curveListP, curveP);
 }
 
 
@@ -367,12 +715,22 @@ static void
 addCurveStartingAtCorner(pixel_outline_type const outline,
                          index_list_type    const cornerList,
                          unsigned int       const cornerSeq,
-                         curve_list_type *  const curveListP) {
+                         curve_list_type *  const curveListP,
+                         curve **           const curCurvePP) {
+/*----------------------------------------------------------------------------
+   Add to the list *curveListP a new curve that starts at the cornerSeq'th
+   corner in outline 'outline' (whose corners are 'cornerList') and
+   goes to the next corner (or the end of the outline if no next corner).
+
+   Furthermore, add that curve to the curve chain whose end is pointed
+   to by *curCurvePP (NULL means chain is empty).
 
+   Don't include beginning and ending slope information for that curve.
+-----------------------------------------------------------------------------*/
     unsigned int const cornerPixelSeq = GET_INDEX(cornerList, cornerSeq);
     
     unsigned int lastPixelSeq;
-    curve_type curve;
+    curve * curveP;
     unsigned int pixelSeq;
     
     if (cornerSeq + 1 >= cornerList.length)
@@ -382,20 +740,23 @@ addCurveStartingAtCorner(pixel_outline_type const outline,
         /* Go through the next corner */
         lastPixelSeq = GET_INDEX(cornerList, cornerSeq + 1);
     
-    curve = new_curve();
+    curveP = new_curve();
 
     for (pixelSeq = cornerPixelSeq; pixelSeq <= lastPixelSeq; ++pixelSeq)
-        append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+        append_pixel(curveP, O_COORDINATE(outline, pixelSeq));
     
+    append_curve(curveListP, curveP);
     {
-        /* Add curve to end of chain */
-        if (!CURVE_LIST_EMPTY(*curveListP)) {
-            curve_type const previousCurve = LAST_CURVE_LIST_ELT(*curveListP);
-            NEXT_CURVE(previousCurve) = curve;
-            PREVIOUS_CURVE(curve)     = previousCurve;
+        /* Add the new curve to the outline chain */
+
+        curve * const oldCurCurveP = *curCurvePP;
+
+        if (oldCurCurveP) {
+            NEXT_CURVE(oldCurCurveP) = curveP;
+            PREVIOUS_CURVE(curveP)   = oldCurCurveP;
         }
+        *curCurvePP = curveP;
     }
-    append_curve(curveListP, curve);
 }
 
 
@@ -421,53 +782,60 @@ divideOutlineWithCorners(pixel_outline_type const outline,
    corner).
 
    Assume there is at least one corner.
+
+   The curves do not have beginning and ending slope information.
 -----------------------------------------------------------------------------*/
     unsigned int const firstCurveSeq = CURVE_LIST_LENGTH(*curveListP);
         /* Index in curve list of the first curve we add */
     unsigned int cornerSeq;
+    curve * curCurveP;
+        /* Pointer to the curve we most recently added for this outline.
+           Null if none
+        */
 
     assert(cornerList.length > 0);
 
+    curCurveP = NULL;  /* No curves in outline chain yet */
+
     if (outline.open) {
-        /* Start with a curve that contains the point up to the first
+        /* Start with a curve that contains the points up to the first
            corner
         */
-        curve_type curve;
+        curve * curveP;
         unsigned int pixelSeq;
         
-        curve = new_curve();
+        curveP = new_curve();
 
         for (pixelSeq = 0; pixelSeq <= GET_INDEX(cornerList, 0); ++pixelSeq)
-            append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+            append_pixel(curveP, O_COORDINATE(outline, pixelSeq));
 
-        append_curve(curveListP, curve);
-    } else
+        append_curve(curveListP, curveP);
+        curCurveP = curveP;  /* Only curve in outline chain now */
+    } else {
         /* We'll pick up the pixels before the first corner at the end */
-
+    }
     /* Add to the list a curve that starts at each corner and goes
        through the following corner, or the end of the outline if
        there is no following corner.  Do it in order of the corners.
     */
     for (cornerSeq = 0; cornerSeq < cornerList.length; ++cornerSeq)
-        addCurveStartingAtCorner(outline, cornerList, cornerSeq, curveListP);
+        addCurveStartingAtCorner(outline, cornerList, cornerSeq, curveListP,
+                                 &curCurveP);
 
     if (!outline.open) {
         /* Come around to the start of the curve list -- add the pixels
            before the first corner to the last curve, and chain the last
            curve to the first one.
         */
-        curve_type const firstCurve =
-            CURVE_LIST_ELT(*curveListP, firstCurveSeq);
-        curve_type const lastCurve  =
-            LAST_CURVE_LIST_ELT(*curveListP);
+        curve * const firstCurveP = CURVE_LIST_ELT(*curveListP, firstCurveSeq);
 
         unsigned int pixelSeq;
 
         for (pixelSeq = 0; pixelSeq <= GET_INDEX(cornerList, 0); ++pixelSeq)
-            append_pixel(lastCurve, O_COORDINATE(outline, pixelSeq));
+            append_pixel(curCurveP, O_COORDINATE(outline, pixelSeq));
 
-        NEXT_CURVE(lastCurve)      = firstCurve;
-        PREVIOUS_CURVE(firstCurve) = lastCurve;
+        NEXT_CURVE(curCurveP)       = firstCurveP;
+        PREVIOUS_CURVE(firstCurveP) = curCurveP;
     }
 }
 
@@ -501,6 +869,9 @@ split_at_corners(pixel_outline_list_type const pixel_list,
    To preserve this information, we return an array of curve_lists, one
    element (which in turn consists of several curves, one between each
    pair of corners) for each element in PIXEL_LIST.
+
+   The curves we return do not have beginning and ending slope
+   information.
 -----------------------------------------------------------------------------*/
     unsigned outlineSeq;
     curve_list_array_type curve_array;
@@ -797,27 +1168,30 @@ spline_linear_enough(spline_type *             const spline,
 /* Forward declaration for recursion */
 
 static spline_list_type *
-fitCurve(curve_type                const curve,
-         const fitting_opts_type * const fitting_opts,
-         at_exception_type *       const exception);
+fitCurve(curve *                   const curveP,
+         vector_type               const begSlope,
+         vector_type               const endSlope,
+         const fitting_opts_type * const fittingOptsP,
+         at_exception_type *       const exceptionP);
 
 
 
 static spline_list_type *
-fit_with_line(curve_type const curve) {
+fitWithLine(curve * const curveP) {
 /*----------------------------------------------------------------------------
-  This routine returns the curve fitted to a straight line in a very
-  simple way: make the first and last points on the curve be the
-  endpoints of the line.  This simplicity is justified because we are
-  called only on very short curves.
+  Return a list of splines that fit curve *curveP in a very simple way:
+  a single spline which is a straight line through the first and last
+  points on the curve.
+
+  This simplicity is useful only on a very short curve.
 -----------------------------------------------------------------------------*/
     spline_type line;
 
     LOG("Fitting with straight line:\n");
 
     SPLINE_DEGREE(line) = LINEARTYPE;
-    START_POINT(line) = CONTROL1(line) = CURVE_POINT(curve, 0);
-    END_POINT(line) = CONTROL2(line) = LAST_CURVE_POINT(curve);
+    START_POINT(line)   = CONTROL1(line) = CURVE_POINT(curveP, 0);
+    END_POINT(line)     = CONTROL2(line) = LAST_CURVE_POINT(curveP);
 
     /* Make sure that this line is never changed to a cubic.  */
     SPLINE_LINEARITY(line) = 0;
@@ -838,98 +1212,123 @@ fit_with_line(curve_type const curve) {
 #define B3(t) CUBE (t)
 
 static spline_type
-fit_one_spline(curve_type          const curve, 
-               at_exception_type * const exception) {
+fitOneSpline(curve *             const curveP, 
+             vector_type         const begSlope,
+             vector_type         const endSlope,
+             at_exception_type * const exceptionP) {
 /*----------------------------------------------------------------------------
-   Our job here is to find alpha1 (and alpha2), where t1_hat (t2_hat) is
-   the tangent to CURVE at the starting (ending) point, such that:
+  Return a spline that fits the points of curve *curveP,
+  with slope 'begSlope' at its beginning and 'endSlope' at its end.
 
-   control1 = alpha1 * t1_hat + starting point
-   control2 = alpha2 * t2_hat + ending_point
+  Make it a cubic spline.
+-----------------------------------------------------------------------------*/
+    /* We already have the start and end points of the spline, so all
+      we need are the control points.  And we know in what direction
+      each control point is from its respective end point, so all we
+      need to figure out is its distance.  (The control point's
+      distance from the end point is an indication of how long the
+      curve goes in its direction).
 
-   and the resulting spline (starting_point .. control1 and control2 ..
-   ending_point) minimizes the least-square error from CURVE.
+      We call the distance from an end point to the associated control
+      point "alpha".
 
-   See pp.57--59 of the Phoenix thesis.
+      We want to find starting and ending alpha that minimize the
+      least-square error in approximating *curveP with the spline.
+
+      How we do that is a complete mystery to me, but the original author
+      said to see pp.57--59 of the Phoenix thesis.  I haven't seen that.
+
+      In our expression of the math here, we use a struct with "beg" and
+      "end" members where the paper uses a matrix with "1" and "2"
+      subscripts, respectively.  A C array is a closer match to a math
+      matrix, but we think the struct is easier to read.
+
+      The B?(t) here corresponds to B_i^3(U_i) there.
+      The Bernstein polynomials of degree n are defined by
+      B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n
 
-   The B?(t) here corresponds to B_i^3(U_i) there.
-   The Bernshte\u in polynomials of degree n are defined by
-   B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n
------------------------------------------------------------------------------*/
-    /* Since our arrays are zero-based, the `C0' and `C1' here correspond
-       to `C1' and `C2' in the paper. 
     */
-    float X_C1_det, C0_X_det, C0_C1_det;
-    float alpha1, alpha2;
+    struct vectorPair {
+        vector_type beg;
+        vector_type end;
+    };
+    struct vectorPair tang;
+
+    float X_Cend_det, Cbeg_X_det, C_det;
     spline_type spline;
-    vector_type start_vector, end_vector;
+    vector_type begVector, endVector;
     unsigned i;
-    vector_type * A;
-    vector_type t1_hat;
-    vector_type t2_hat;
-    float C[2][2] = { { 0.0, 0.0 }, { 0.0, 0.0 } };
-    float X[2] = { 0.0, 0.0 };
+    struct vectorPair * A;  /* malloc'ed array */
+        /* I don't know the meaning of this array, but it is one entry for
+           each point in the curve (A[i] is for the ith point in the curve).
+        */
+    struct {
+        struct { float beg; float end; } beg;
+        struct { float beg; float end; } end;
+    } C;
+    struct { float beg; float end; } X;
 
-    t1_hat = *CURVE_START_TANGENT(curve);  /* initial value */
-    t2_hat = *CURVE_END_TANGENT(curve);    /* initial value */
+    tang.beg = begSlope; tang.end = endSlope;
 
-    MALLOCARRAY_NOFAIL(A, CURVE_LENGTH(curve) * 2);
+    MALLOCARRAY_NOFAIL(A, CURVE_LENGTH(curveP));
 
-    START_POINT(spline) = CURVE_POINT(curve, 0);
-    END_POINT(spline)   = LAST_CURVE_POINT(curve);
-    start_vector = make_vector(START_POINT(spline));
-    end_vector   = make_vector(END_POINT(spline));
+    BEG_POINT(spline) = CURVE_POINT(curveP, 0);
+    END_POINT(spline) = LAST_CURVE_POINT(curveP);
+    begVector = make_vector(BEG_POINT(spline));
+    endVector = make_vector(END_POINT(spline));
 
-    for (i = 0; i < CURVE_LENGTH(curve); ++i) {
-        A[(i << 1) + 0] = Vmult_scalar(t1_hat, B1(CURVE_T(curve, i)));
-        A[(i << 1) + 1] = Vmult_scalar(t2_hat, B2(CURVE_T(curve, i)));
+    for (i = 0; i < CURVE_LENGTH(curveP); ++i) {
+        A[i].beg = Vmult_scalar(tang.beg, B1(CURVE_T(curveP, i)));
+        A[i].end = Vmult_scalar(tang.end, B2(CURVE_T(curveP, i)));
     }
 
-    for (i = 0; i < CURVE_LENGTH(curve); ++i) {
+    C.beg.beg = 0.0; C.beg.end = 0.0; C.end.end = 0.0;  /* initial value */
+
+    X.beg = 0.0; X.end = 0.0; /* initial value */
+
+    for (i = 0; i < CURVE_LENGTH(curveP); ++i) {
+        struct vectorPair * const AP = &A[i];
         vector_type temp, temp0, temp1, temp2, temp3;
-        vector_type * Ai = A + (i << 1);
 
-        C[0][0] += Vdot(Ai[0], Ai[0]);
-        C[0][1] += Vdot(Ai[0], Ai[1]);
-        /* C[1][0] = C[0][1] (this is assigned outside the loop)  */
-        C[1][1] += Vdot(Ai[1], Ai[1]);
+        C.beg.beg += Vdot(AP->beg, AP->beg);
+        C.beg.end += Vdot(AP->beg, AP->end);
+        /* C.end.beg = Vdot(AP->end, AP->beg) is done outside of loop */
+        C.end.end += Vdot(AP->end, AP->end);
 
         /* Now the right-hand side of the equation in the paper.  */
-        temp0 = Vmult_scalar(start_vector, B0(CURVE_T(curve, i)));
-        temp1 = Vmult_scalar(start_vector, B1(CURVE_T(curve, i)));
-        temp2 = Vmult_scalar(end_vector, B2(CURVE_T(curve, i)));
-        temp3 = Vmult_scalar(end_vector, B3(CURVE_T(curve, i)));
+        temp0 = Vmult_scalar(begVector, B0(CURVE_T(curveP, i)));
+        temp1 = Vmult_scalar(begVector, B1(CURVE_T(curveP, i)));
+        temp2 = Vmult_scalar(endVector, B2(CURVE_T(curveP, i)));
+        temp3 = Vmult_scalar(endVector, B3(CURVE_T(curveP, i)));
 
         temp = make_vector(
-            Vsubtract_point(CURVE_POINT(curve, i),
+            Vsubtract_point(CURVE_POINT(curveP, i),
                             Vadd(temp0, Vadd(temp1, Vadd(temp2, temp3)))));
 
-        X[0] += Vdot(temp, Ai[0]);
-        X[1] += Vdot(temp, Ai[1]);
+        X.beg += Vdot(temp, AP->beg);
+        X.end += Vdot(temp, AP->end);
     }
     free(A);
 
-    C[1][0] = C[0][1];
+    C.end.beg = C.beg.end;
     
-    X_C1_det = X[0] * C[1][1] - X[1] * C[0][1];
-    C0_X_det = C[0][0] * X[1] - C[0][1] * X[0];
-    C0_C1_det = C[0][0] * C[1][1] - C[1][0] * C[0][1];
-    if (C0_C1_det == 0.0) {
-        LOG ("zero determinant of C0*C1");
-        at_exception_fatal(exception, "zero determinant of C0*C1");
-        goto cleanup;
-    }
-
-    alpha1 = X_C1_det / C0_C1_det;
-    alpha2 = C0_X_det / C0_C1_det;
-
-    CONTROL1(spline) = Vadd_point(START_POINT(spline),
-                                  Vmult_scalar(t1_hat, alpha1));
-    CONTROL2(spline) = Vadd_point(END_POINT(spline),
-                                  Vmult_scalar(t2_hat, alpha2));
-    SPLINE_DEGREE(spline) = CUBICTYPE;
-
-cleanup:
+    X_Cend_det  = X.beg * C.end.end - X.end * C.beg.end;
+    Cbeg_X_det  = C.beg.beg * X.end - C.beg.end * X.beg;
+    C_det = C.beg.beg * C.end.end - C.end.beg * C.beg.end;
+    if (C_det == 0.0) {
+        LOG("zero determinant of C matrix");
+        at_exception_fatal(exceptionP, "zero determinant of C matrix");
+    } else {
+        struct { float beg; float end; } alpha;  /* constant */
+        alpha.beg = X_Cend_det / C_det;
+        alpha.end = Cbeg_X_det / C_det;
+
+        CONTROL1(spline) = Vadd_point(BEG_POINT(spline),
+                                      Vmult_scalar(tang.beg, alpha.beg));
+        CONTROL2(spline) = Vadd_point(END_POINT(spline),
+                                      Vmult_scalar(tang.end, alpha.end));
+        SPLINE_DEGREE(spline) = CUBICTYPE;
+    }        
     return spline;
 }
 
@@ -951,214 +1350,538 @@ logSplineFit(spline_type const spline) {
 
 
 
-static spline_list_type *
-fit_with_least_squares(curve_type                const curve,
-                       const fitting_opts_type * const fitting_opts,
-                       at_exception_type *       const exception) {
+static vector_type
+findHalfTangentBeg(curve *      const curveP,
+                   unsigned int const tangentSurround) {
 /*----------------------------------------------------------------------------
-  The least squares method is well described in Schneider's thesis.
-  Briefly, we try to fit the entire curve with one spline.  If that
-  fails, we subdivide the curve. 
+  Find the slope in the vicinity of the beginning of the curve
+  *curveP.
+
+  To wit, this is the mean slope between the first point on the curve and
+  each of the 'tangentSurround' following points, up to half the curve.
+
+  For example, if 'tangentSurround' is 3 and the curve is 10 points
+  long, we imagine a line through Point 0 and Point 1, another through
+  Point 0 and Point 2, and a third through Point 0 and Point 3.  We
+  return the mean of the slopes of those 3 lines.
 -----------------------------------------------------------------------------*/
-    float error;
-    float best_error;
-    spline_type spline;
-    spline_type best_spline;
-    spline_list_type * spline_list;
-    unsigned int worst_point;
-    float previous_error;
-    
-    best_error = FLT_MAX;  /* initial value */
-    previous_error = FLT_MAX;  /* initial value */
-    spline_list = NULL;  /* initial value */
-    worst_point = 0;  /* initial value */
+    float_coord const tangentPoint = CURVE_POINT(curveP, 0);
+    vector_type const zeroZero = { 0.0, 0.0 };
+    unsigned int const surround =
+        MIN(CURVE_LENGTH(curveP) / 2, tangentSurround);
 
-    LOG ("\nFitting with least squares:\n");
-    
-    /* Phoenix reduces the number of points with a ``linear spline
-       technique''.  But for fitting letterforms, that is
-       inappropriate.  We want all the points we can get.
-    */
+    unsigned int p;
+    vector_type sum;
+    vector_type mean;
+    unsigned int n;
+
+    for (p = 0, n = 0, sum = zeroZero; p < surround; ++p) {
+        unsigned int const thisIndex = p + 1;
+        float_coord  const thisPoint = CURVE_POINT(curveP, thisIndex);
+
+        /* Perhaps we should weight the tangent from `thisPoint' by some
+           factor dependent on the distance from the tangent point.
+        */
+        sum = Vadd(sum, Pdirection(thisPoint, tangentPoint));
+        ++n;
+    }
+
+    mean = Vmult_scalar(sum, 1.0 / n);
+
+    return mean;
+}
+
+
+
+static vector_type
+findHalfTangentEnd(curve *      const curveP,
+                   unsigned int const tangentSurround) {
+/*----------------------------------------------------------------------------
+  Find the slope in the vicinity of the end of the curve
+  *curveP.
+
+  This is analogous to findHalfTangentBeg(), but at the other end of the
+  curve.
+-----------------------------------------------------------------------------*/
+    float_coord const tangentPoint =
+        CURVE_POINT(curveP, CURVE_LENGTH(curveP) - 1);
+    vector_type const zeroZero = { 0.0, 0.0 };
+    unsigned int const surround =
+        MIN(CURVE_LENGTH(curveP) / 2, tangentSurround);
+
+    unsigned int p;
+    vector_type sum;
+    vector_type mean;
+    unsigned int n;
+
+    for (p = 0, n = 0, sum = zeroZero; p < surround; ++p) {
+        unsigned int const thisIndex = CURVE_LENGTH(curveP) - 1 - p;
+        float_coord  const thisPoint = CURVE_POINT(curveP, thisIndex);
+
+        sum = Vadd(sum, Pdirection(tangentPoint, thisPoint));
+        ++n;
+    }
+
+    mean = Vmult_scalar(sum, 1.0 / n);
+
+    return mean;
+}
+
+
+
+static vector_type
+findHalfTangent(bool         const toStartPoint,
+                curve *      const curveP,
+                unsigned int const tangentSurround) {
+
+    if (toStartPoint)
+        return findHalfTangentBeg(curveP, tangentSurround);
+    else
+        return findHalfTangentEnd(curveP, tangentSurround);
+}
+
+
+
+static void
+findTangent(curve *       const curveP,
+            bool          const toStartPoint,
+            curve *       const adjacentCurveP,
+            unsigned int  const tangentSurroundArg,
+            vector_type * const tangentP) {
+/*----------------------------------------------------------------------------
+  Find an approximation to the slope of *curveP (i.e. slope of tangent
+  line) at an endpoint (the first point if 'toStartPoint' is true,
+  else the last).
+
+  If 'adjacentCurveP' is non-null, consider points on the adjacent
+  curve to *curveP.  The adjacent curve is *adjacentCurveP.  Adjacent
+  means the previous curve in the outline chain for the slope at the
+  start point ('toStartPoint' == true), the next curve otherwise.
+  If *curveP is cyclic, then it is its own adjacent curve.
+  
+  It is important to compute an accurate approximation, because the
+  control points that we eventually decide upon to fit the curve will
+  be placed on the half-lines defined by the slopes and endpoints, and
+  we never recompute the tangent after this.
+-----------------------------------------------------------------------------*/
+    vector_type slope;
+    unsigned int tangentSurround;
+
+    LOG2("  tangent to %s of curve %lx: ",
+         toStartPoint ? "start" : "end", (unsigned long)curveP);
+
+    tangentSurround = tangentSurroundArg;  /* initial value */
+    do {
+        slope = findHalfTangent(toStartPoint, curveP, tangentSurround);
+
+        if (adjacentCurveP) {
+            vector_type const slopeAdj =
+                findHalfTangent(!toStartPoint, adjacentCurveP,
+                                tangentSurround);
+               
+            LOG3("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ",
+                 slopeAdj.dx, slopeAdj.dy, slopeAdj.dz);
+            slope = Vmult_scalar(Vadd(slope, slopeAdj), 0.5);
+        }
+        --tangentSurround;
+    } while (slope.dx == 0.0 && slope.dy == 0.0);
+
+    *tangentP = slope;
     
-    /* It makes no difference whether we first set the `t' values or
-       find the tangents.  This order makes the documentation a little
-       more coherent.
-    */
+    LOG3("(%.3f,%.3f,%.3f).\n",
+         tangentP->dx, tangentP->dy, tangentP->dz);
+}
 
-    LOG("Finding tangents:\n");
-    find_tangent(curve, /* to_start */ true,  /* cross_curve */ false,
-                 fitting_opts->tangent_surround);
-    find_tangent(curve, /* to_start */ false, /* cross_curve */ false,
-                 fitting_opts->tangent_surround);
 
-    set_initial_parameter_values(curve);
 
-    /* Now we loop, subdividing, until CURVE has been fit.  */
-    while (true) {
-        float error;
+static void
+findError(curve *             const curveP,
+          spline_type         const spline,
+          float *             const errorP,
+          unsigned int *      const worstPointP,
+          at_exception_type * const exceptionP) {
+/*----------------------------------------------------------------------------
+  Tell how good a fit 'spline' is for *curveP.
+  
+  Return the error (maximum Euclidian distance between a point on
+  *curveP and the corresponding point on 'spline') as *errorP and the
+  sequence number of the point on the curve where the error is
+  greatest as *worstPointP.
+
+  If there are multiple equally bad points, return an arbitrary one of
+  them as *worstPointP.
+-----------------------------------------------------------------------------*/
+    unsigned int thisPoint;
+    float totalError;
+    float worstError;
+    unsigned int worstPoint;
 
-        spline = fit_one_spline(curve, exception);
-        best_spline = spline;
-        if (at_exception_got_fatal(exception))
-            goto cleanup;
+    assert(CURVE_LENGTH(curveP) > 0);
 
-        logSplineFit(spline);
+    totalError = 0.0;  /* initial value */
+    worstError = FLT_MIN; /* initial value */
+    worstPoint = 0;
         
-        if (SPLINE_DEGREE(spline) == LINEARTYPE)
-            break;
-
-        error = find_error(curve, spline, &worst_point, exception);
-        if (error <= previous_error) {
-            best_error  = error;
-            best_spline = spline;
+    for (thisPoint = 0; thisPoint < CURVE_LENGTH(curveP); ++thisPoint) {
+        float_coord const curvePoint = CURVE_POINT(curveP, thisPoint);
+        float const t = CURVE_T(curveP, thisPoint);
+        float_coord const splinePoint = evaluate_spline(spline, t);
+        float const thisError = distance(curvePoint, splinePoint);
+        if (thisError >= worstError) {
+            worstPoint = thisPoint;
+            worstError = thisError;
         }
-        break;
+        totalError += thisError;
     }
 
-    if (SPLINE_DEGREE(spline) == LINEARTYPE) {
-        spline_list = new_spline_list_with_spline(spline);
-        LOG1("Accepted error of %.3f.\n", error);
-        return spline_list;
+    if (epsilon_equal(totalError, 0.0))
+        LOG("  Every point fits perfectly.\n");
+    else {
+        LOG5("  Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n",
+             CURVE_POINT(curveP, worstPoint).x,
+             CURVE_POINT(curveP, worstPoint).y,
+             CURVE_POINT(curveP, worstPoint).z,
+             worstPoint, worstError);
+        LOG1("  Total error was %.3f.\n", totalError);
+        LOG2("  Average error (over %u points) was %.3f.\n",
+                 CURVE_LENGTH(curveP), totalError / CURVE_LENGTH(curveP));
     }
+    assert(worstPoint < CURVE_LENGTH(curveP));
+    *errorP      = worstError;
+    *worstPointP = worstPoint;
+}
+
+
+
+static void
+setInitialParameterValues(curve * const curveP) {
+/*----------------------------------------------------------------------------
+   Fill in the 't' values in *curveP.
 
-    /* Go back to the best fit.  */
-    spline = best_spline;
-    error = best_error;
+   The t value for point P on a curve is the distance P is along the
+   curve from the initial point, normalized so the entire curve is
+   length 1.0 (i.e. t of the initial point is 0.0; t of the final
+   point is 1.0).
 
-    if (error < fitting_opts->error_threshold && !CURVE_CYCLIC(curve)) {
-        /* The points were fitted with a spline.  We end up here
-           whenever a fit is accepted.  We have one more job: see if
-           the ``curve'' that was fit should really be a straight
-           line.
+   There are a lot of curves that pass through the points indicated by
+   *curveP, but for practical computation of t, we just take the
+   piecewise linear locus that runs through all of them.  That means
+   we can just step through *curveP, adding up the distance from one
+   point to the next to get the t value for each point.
+
+   This is the "chord-length parameterization" method, which is
+   described in Plass & Stone.
+-----------------------------------------------------------------------------*/
+    unsigned int p;
+
+    LOG("\nAssigning initial t values:\n  ");
+
+    CURVE_T(curveP, 0) = 0.0;
+
+    for (p = 1; p < CURVE_LENGTH(curveP); ++p) {
+        float_coord const point      = CURVE_POINT(curveP, p);
+        float_coord const previous_p = CURVE_POINT(curveP, p - 1);
+        float const d = distance(point, previous_p);
+        CURVE_T(curveP, p) = CURVE_T(curveP, p - 1) + d;
+    }
+
+    assert(LAST_CURVE_T(curveP) != 0.0);
+
+    /* Normalize to a curve length of 1.0 */
+
+    for (p = 1; p < CURVE_LENGTH(curveP); ++p)
+        CURVE_T(curveP, p) = CURVE_T(curveP, p) / LAST_CURVE_T(curveP);
+
+    log_entire_curve(curveP);
+}
+
+
+
+static void
+subdivideCurve(curve *                   const curveP,
+               unsigned int              const subdivisionIndex,
+               const fitting_opts_type * const fittingOptsP,
+               curve **                  const leftCurvePP,
+               curve **                  const rghtCurvePP,
+               vector_type *             const joinSlopeP) {
+/*----------------------------------------------------------------------------
+  Split curve *curveP into two, at 'subdivisionIndex'.  (Actually,
+  leave *curveP alone, but return as *leftCurvePP and *rghtCurvePP
+  two new curves that are the pieces).
+  
+  Return as *joinSlopeP what should be the slope where the subcurves
+  join, i.e. the slope of the end of the left subcurve and of the start
+  of the right subcurve.
+
+  To be precise, the point with sequence number 'subdivisionIndex'
+  becomes the first pixel of the right-hand curve.
+-----------------------------------------------------------------------------*/
+    curve * leftCurveP;
+    curve * rghtCurveP;
+
+    leftCurveP = new_curve();
+    rghtCurveP = new_curve();
+
+    LOG4("  Subdividing curve %lx into %lx and %lx at point #%u\n",
+         (unsigned long)curveP,
+         (unsigned long)leftCurveP, (unsigned long)rghtCurveP,
+         subdivisionIndex);
+
+    /* The last point of the left-hand curve will also be the first
+       point of the right-hand curve.
+    */
+    assert(subdivisionIndex < CURVE_LENGTH(curveP));
+    CURVE_LENGTH(leftCurveP) = subdivisionIndex + 1;
+    CURVE_LENGTH(rghtCurveP) = CURVE_LENGTH(curveP) - subdivisionIndex;
+
+    MALLOCARRAY_NOFAIL(leftCurveP->point_list, CURVE_LENGTH(leftCurveP));
+    memcpy(leftCurveP->point_list, &curveP->point_list[0],
+           CURVE_LENGTH(leftCurveP) * sizeof(curveP->point_list[0]));
+
+    MALLOCARRAY_NOFAIL(rghtCurveP->point_list, CURVE_LENGTH(rghtCurveP));
+    memcpy(rghtCurveP->point_list, &curveP->point_list[subdivisionIndex],
+           CURVE_LENGTH(rghtCurveP) * sizeof(curveP->point_list[0]));
+
+    /* We have to set up the two curves before finding the slope at
+       the subdivision point.  The slope at that point must be the
+       same for both curves, or noticeable bumps will occur in the
+       character.  But we want to use information on both sides of the
+       point to compute the slope, hence we use adjacentCurveP.
+    */
+    findTangent(leftCurveP,
+                /* toStartPoint: */   false,
+                /* adjacentCurveP: */ rghtCurveP,
+                fittingOptsP->tangent_surround, joinSlopeP);
+
+    *leftCurvePP = leftCurveP;
+    *rghtCurvePP = rghtCurveP;
+}
+
+
+
+static spline_list_type *
+leftRightConcat(const spline_list_type *  const leftSplineListP,
+                const spline_list_type *  const rghtSplineListP,
+                at_exception_type *       const exceptionP) {
+/*----------------------------------------------------------------------------
+   Return a spline list which is the concatenation of the spline lists
+   obtained by splitting a curve in two and fitting each independently.
+   NULL for a spline list pointer means Caller was unable to fit a list
+   of splines to that side of the curve.
+-----------------------------------------------------------------------------*/
+    spline_list_type * retval;
+                
+    retval = new_spline_list();
+
+    if (leftSplineListP == NULL) {
+        LOG("Could not fit spline to left curve.\n");
+        at_exception_warning(exceptionP, "Could not fit left spline list");
+    } else
+        concat_spline_lists(retval, *leftSplineListP);
+    
+    if (rghtSplineListP == NULL) {
+        LOG("Could not fit spline to right curve.\n");
+        at_exception_warning(exceptionP, "Could not fit right spline list");
+    } else
+        concat_spline_lists(retval, *rghtSplineListP);
+
+    return retval;
+}
+
+
+
+static unsigned int
+divisionPoint(curve *      const curveP,
+              unsigned int const worstFitPoint) {
+/*----------------------------------------------------------------------------
+   Return the sequence number of the point at which we should divide
+   curve *curveP for the purpose of doing a separate fit of each side,
+   assuming the point which least matches a single spline is sequence
+   number 'worstFitPoint'.
+
+   We get as close as we can to that while still having at least two
+   points on each side.
+
+   Assume the curve is at least 4 points long.
+
+   The return value is the sequence number of the first point of the
+   second (right-hand) subcurve.
+-----------------------------------------------------------------------------*/
+    assert(CURVE_LENGTH(curveP) >= 4);
+
+    return MAX(2, MIN(worstFitPoint, CURVE_LENGTH(curveP) - 2));
+}
+
+
+
+static spline_list_type *
+divideAndFit(curve *                   const curveP,
+             vector_type               const begSlope,
+             vector_type               const endSlope,
+             unsigned int              const subdivisionIndex,
+             const fitting_opts_type * const fittingOptsP,
+             at_exception_type *       const exceptionP) {
+/*----------------------------------------------------------------------------
+  Same as fitWithLeastSquares() (i.e. return a list of splines that fit
+  the curve *curveP), except assuming no single spline will fit the
+  entire curve.
+
+  Divide it into two curves at 'subdivisionIndex' and fit each
+  separately to a list of splines.  Return the concatenation of those
+  spline lists.
+
+  Assume 'subdivisionIndex' leaves at least two pixels on each side.
+-----------------------------------------------------------------------------*/
+    spline_list_type * retval;
+    curve * leftCurveP;
+        /* The beginning (lower indexes) subcurve */
+    curve * rghtCurveP;
+        /* The other subcurve */
+    vector_type joinSlope;
+        /* The slope of the end of the left subcurve and start of the right
+           subcurve.
         */
-        if (spline_linear_enough(&spline, curve, fitting_opts)) {
-            SPLINE_DEGREE(spline) = LINEARTYPE;
-            LOG("Changed to line.\n");
+    spline_list_type * leftSplineListP;
+    
+    assert(subdivisionIndex > 1);
+    assert(subdivisionIndex < CURVE_LENGTH(curveP)-1);
+    subdivideCurve(curveP, subdivisionIndex, fittingOptsP,
+                   &leftCurveP, &rghtCurveP, &joinSlope);
+
+    leftSplineListP = fitCurve(leftCurveP, begSlope, joinSlope,
+                               fittingOptsP, exceptionP);
+
+    if (!at_exception_got_fatal(exceptionP)) {
+        spline_list_type * rghtSplineListP;
+
+        rghtSplineListP = fitCurve(rghtCurveP, joinSlope, endSlope,
+                                   fittingOptsP, exceptionP);
+
+        if (!at_exception_got_fatal(exceptionP)) {
+            if (leftSplineListP == NULL && rghtSplineListP == NULL)
+                retval = NULL;
+            else
+                retval = leftRightConcat(leftSplineListP, rghtSplineListP,
+                                         exceptionP);
+
+            if (rghtSplineListP) {
+                free_spline_list(*rghtSplineListP);
+                free(rghtSplineListP);
+            }
         }
-        spline_list = new_spline_list_with_spline(spline);
-        LOG1("Accepted error of %.3f.\n", error);
-    } else {
-        /* We couldn't fit the curve acceptably, so subdivide.  */
-        unsigned subdivision_index;
-        spline_list_type * left_spline_list;
-        spline_list_type * right_spline_list;
-        curve_type left_curve, right_curve;
-
-        left_curve  = new_curve();
-        right_curve = new_curve();
-
-        /* Insert 'left_curve', then 'right_curve' after 'curve' in the list */
-        NEXT_CURVE(right_curve) = NEXT_CURVE(curve);
-        PREVIOUS_CURVE(right_curve) = left_curve;
-        NEXT_CURVE(left_curve) = right_curve;
-        PREVIOUS_CURVE(left_curve) = curve;
-        NEXT_CURVE(curve) = left_curve;
-
-        LOG1("\nSubdividing (error %.3f):\n", error);
-        LOG3("  Original point: (%.3f,%.3f), #%u.\n",
-             CURVE_POINT (curve, worst_point).x,
-             CURVE_POINT (curve, worst_point).y, worst_point);
-        subdivision_index = worst_point;
-        LOG3 ("  Final point: (%.3f,%.3f), #%u.\n",
-              CURVE_POINT (curve, subdivision_index).x,
-              CURVE_POINT (curve, subdivision_index).y, subdivision_index);
-
-        /* The last point of the left-hand curve will also be the first
-           point of the right-hand curve.  */
-        CURVE_LENGTH(left_curve)  = subdivision_index + 1;
-        CURVE_LENGTH(right_curve) = CURVE_LENGTH(curve) - subdivision_index;
-        left_curve->point_list = curve->point_list;
-        right_curve->point_list = curve->point_list + subdivision_index;
-
-        /* We want to use the tangents of the curve which we are
-           subdividing for the start tangent for left_curve and the
-           end tangent for right_curve.
-        */
-        CURVE_START_TANGENT(left_curve) = CURVE_START_TANGENT(curve);
-        CURVE_END_TANGENT(right_curve)  = CURVE_END_TANGENT(curve);
-
-        /* We have to set up the two curves before finding the tangent at
-           the subdivision point.  The tangent at that point must be the
-           same for both curves, or noticeable bumps will occur in the
-           character.  But we want to use information on both sides of the
-           point to compute the tangent, hence cross_curve = true.
-        */
-        find_tangent(left_curve, /* to_start_point: */ false,
-                     /* cross_curve: */ true, fitting_opts->tangent_surround);
-        CURVE_START_TANGENT(right_curve) = CURVE_END_TANGENT(left_curve);
-
-        /* Now that we've set up the curves, we can fit them.  */
-        left_spline_list = fitCurve(left_curve, fitting_opts, exception);
-        if (at_exception_got_fatal(exception))
-            /* TODO: Memory allocated for left_curve and right_curve
-               will leak.*/
-            goto cleanup;
+        if (leftSplineListP) {
+            free_spline_list(*leftSplineListP);
+            free(leftSplineListP);
+        }
+    }
 
-        right_spline_list = fitCurve(right_curve, fitting_opts, exception);
-        /* TODO: Memory allocated for left_curve and right_curve
-           will leak.*/
-        if (at_exception_got_fatal(exception))
-            goto cleanup;
-        
-        /* Neither of the subdivided curves could be fit, so fail.  */
-        if (left_spline_list == NULL && right_spline_list == NULL)
-            return NULL;
+    free_curve(leftCurveP);
+    free_curve(rghtCurveP);
 
-        /* Put the two together (or whichever of them exist).  */
-        spline_list = new_spline_list();
+    return retval;
+}
 
-        if (left_spline_list == NULL) {
-            LOG1("Could not fit spline to left curve (%lx).\n",
-                 (unsigned long) left_curve);
-            at_exception_warning(exception, "Could not fit left spline list");
-        } else {
-            concat_spline_lists(spline_list, *left_spline_list);
-            free_spline_list(*left_spline_list);
-            free(left_spline_list);
+
+
+static spline_list_type *
+fitWithLeastSquares(curve *                   const curveP,
+                    vector_type               const begSlope,
+                    vector_type               const endSlope,
+                    const fitting_opts_type * const fittingOptsP,
+                    at_exception_type *       const exceptionP) {
+/*----------------------------------------------------------------------------
+  The least squares method is well described in Schneider's thesis.
+  Briefly, we try to fit the entire curve with one spline.  If that
+  fails, we subdivide the curve. 
+-----------------------------------------------------------------------------*/
+    spline_list_type * retval;
+    spline_type spline;
+    
+    LOG("\nFitting with least squares:\n");
+    
+    /* Phoenix reduces the number of points with a "linear spline
+       technique."  But for fitting letterforms, that is
+       inappropriate.  We want all the points we can get.
+    */
+    
+    setInitialParameterValues(curveP);
+
+    if (CURVE_CYCLIC(curveP) && CURVE_LENGTH(curveP) < 4) {
+        unsigned i;
+        for (i = 0; i < CURVE_LENGTH(curveP); ++i) {
+            float_coord const point = CURVE_POINT(curveP, i);
+            fprintf(stderr, "point %u = (%f, %f)\n", i, point.x, point.y);
         }
+    }
+
+    /* Try a single spline over whole curve */
+
+    spline = fitOneSpline(curveP, begSlope, endSlope, exceptionP);
+    if (!at_exception_got_fatal(exceptionP)) {
+        float error;
+        unsigned int worstPoint;
+
+        logSplineFit(spline);
         
-        if (right_spline_list == NULL) {
-            LOG1("Could not fit spline to right curve (%lx).\n",
-                 (unsigned long) right_curve);
-            at_exception_warning(exception, "Could not fit right spline list");
+        findError(curveP, spline, &error, &worstPoint, exceptionP);
+        assert(worstPoint < CURVE_LENGTH(curveP));
+
+        if (error < fittingOptsP->error_threshold && !CURVE_CYCLIC(curveP)) {
+            /* The points were fitted adequately with a spline.  But
+               see if the "curve" that was fit should really just be a
+               straight line.
+            */
+            if (spline_linear_enough(&spline, curveP, fittingOptsP)) {
+                SPLINE_DEGREE(spline) = LINEARTYPE;
+                LOG("Changed to line.\n");
+            }
+            retval = new_spline_list_with_spline(spline);
+            LOG1("Accepted error of %.3f.\n", error);
         } else {
-            concat_spline_lists(spline_list, *right_spline_list);
-            free_spline_list(*right_spline_list);
-            free(right_spline_list);
+            /* We couldn't fit the curve acceptably with a single spline,
+               so divide into two curves and try to fit each separately.
+            */
+            unsigned int const divIndex = divisionPoint(curveP, worstPoint);
+            LOG1("\nSubdividing at point #%u\n", divIndex);
+            LOG4("  Worst match point: (%.3f,%.3f), #%u.  Error %.3f\n",
+                 CURVE_POINT(curveP, worstPoint).x,
+                 CURVE_POINT(curveP, worstPoint).y, worstPoint, error);
+
+            retval = divideAndFit(curveP, begSlope, endSlope, divIndex,
+                                  fittingOptsP, exceptionP);
         }
-        if (CURVE_END_TANGENT(left_curve))
-            free(CURVE_END_TANGENT(left_curve));
-        free(left_curve);
-        free(right_curve);
-    }
-cleanup:
+    } else
+        retval = NULL; /* quiet compiler warning */
 
-    return spline_list;
+    return retval;
 }
 
 
 
 static spline_list_type *
-fitCurve(curve_type                const curve,
+fitCurve(curve *                   const curveP,
+         vector_type               const begSlope,
+         vector_type               const endSlope,
          const fitting_opts_type * const fittingOptsP,
-         at_exception_type *       const exception) {
+         at_exception_type *       const exceptionP) {
 /*----------------------------------------------------------------------------
   Transform a set of locations to a list of splines (the fewer the
-  better).  We are guaranteed that CURVE does not contain any corners.
+  better).  We are guaranteed that *curveP does not contain any corners.
   We return NULL if we cannot fit the points at all.
 -----------------------------------------------------------------------------*/
     spline_list_type * fittedSplinesP;
 
-    if (CURVE_LENGTH(curve) < 2) {
-        LOG("Tried to fit curve with less than two points");
-        at_exception_warning(exception, 
+    if (CURVE_LENGTH(curveP) < 2) {
+        LOG("Tried to fit curve with fewer than two points");
+        at_exception_warning(exceptionP, 
                              "Tried to fit curve with less than two points");
         fittedSplinesP = NULL;
-    } else if (CURVE_LENGTH(curve) < 4)
-        fittedSplinesP = fit_with_line(curve);
+    } else if (CURVE_LENGTH(curveP) < 4)
+        fittedSplinesP = fitWithLine(curveP);
     else
         fittedSplinesP =
-            fit_with_least_squares(curve, fittingOptsP, exception);
+            fitWithLeastSquares(curveP, begSlope, endSlope, fittingOptsP,
+                                exceptionP);
 
     return fittedSplinesP;
 }
@@ -1185,13 +1908,24 @@ fitCurves(curve_list_type           const curveList,
          curveSeq < curveList.length && !at_exception_got_fatal(exceptionP);
          ++curveSeq) {
 
-        curve_type const currentCurve = CURVE_LIST_ELT(curveList, curveSeq);
+        curve * const curveP = CURVE_LIST_ELT(curveList, curveSeq);
 
+        vector_type begSlope, endSlope;
         spline_list_type * curveSplinesP;
 
-        LOG1("\nFitting curve #%u:\n", curveSeq);
+        LOG2("\nFitting curve #%u (%lx):\n", curveSeq, (unsigned long)curveP);
 
-        curveSplinesP = fitCurve(currentCurve, fittingOptsP, exceptionP);
+        LOG("Finding tangents:\n");
+        findTangent(curveP, /* toStart */ true,
+                    CURVE_CYCLIC(curveP) ? curveP : NULL,
+                    fittingOptsP->tangent_surround,
+                    &begSlope);
+        findTangent(curveP, /* toStart */ false,
+                    CURVE_CYCLIC(curveP) ? curveP : NULL,
+                    fittingOptsP->tangent_surround, &endSlope);
+
+        curveSplinesP = fitCurve(curveP, begSlope, endSlope, fittingOptsP,
+                                 exceptionP);
         if (!at_exception_got_fatal(exceptionP)) {
             if (curveSplinesP == NULL) {
                 LOG1("Could not fit curve #%u", curveSeq);
@@ -1369,7 +2103,6 @@ fit_outlines_to_splines(pixel_outline_list_type  const pixelOutlineList,
                        exception, notifyProgress, progressData,
                        testCancel, testcancelData, splineListArrayP);
 
-
     free_curve_list_array(&curveListArray, notifyProgress, progressData);
     
     flush_log_output();
@@ -1377,548 +2110,3 @@ fit_outlines_to_splines(pixel_outline_list_type  const pixelOutlineList,
 
 
 
-
-static void
-find_vectors(unsigned int       const test_index,
-             pixel_outline_type const outline,
-             vector_type *      const in,
-             vector_type *      const out,
-             unsigned int       const corner_surround) {
-/*----------------------------------------------------------------------------
-  Return the difference vectors coming in and going out of the outline
-  OUTLINE at the point whose index is TEST_INDEX.  In Phoenix,
-  Schneider looks at a single point on either side of the point we're
-  considering.  That works for him because his points are not touching.
-  But our points *are* touching, and so we have to look at
-  `corner_surround' points on either side, to get a better picture of
-  the outline's shape.
------------------------------------------------------------------------------*/
-    int i;
-    unsigned n_done;
-    pm_pixelcoord const candidate = O_COORDINATE(outline, test_index);
-
-    in->dx  = in->dy  = in->dz  = 0.0;
-    out->dx = out->dy = out->dz = 0.0;
-    
-    /* Add up the differences from p of the `corner_surround' points
-       before p.
-    */
-    for (i = O_PREV(outline, test_index), n_done = 0;
-         n_done < corner_surround;
-         i = O_PREV(outline, i), ++n_done)
-        *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate));
-    
-    /* And the points after p. */
-    for (i = O_NEXT (outline, test_index), n_done = 0;
-         n_done < corner_surround;
-         i = O_NEXT(outline, i), ++n_done)
-        *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate));
-}
-
-
-
-/* Remove adjacent points from the index list LIST.  We do this by first
-   sorting the list and then running through it.  Since these lists are
-   quite short, a straight selection sort (e.g., p.139 of the Art of
-   Computer Programming, vol.3) is good enough.  LAST_INDEX is the index
-   of the last pixel on the outline, i.e., the next one is the first
-   pixel. We need this for checking the adjacency of the last corner.
-
-   We need to do this because the adjacent corners turn into
-   two-pixel-long curves, which can only be fit by straight lines.  */
-
-static void
-remove_adjacent_corners (index_list_type *list, unsigned last_index,
-             bool remove_adj_corners,
-             at_exception_type * exception)
-             
-{
-  unsigned j;
-  unsigned last;
-  index_list_type new_list = new_index_list ();
-
-  for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--)
-    {
-      unsigned search;
-      unsigned temp;
-      /* Find maximal element below `j'.  */
-      unsigned max_index = j;
-
-      for (search = 0; search < j; search++)
-        if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index))
-          max_index = search;
-
-      if (max_index != j)
-        {
-          temp = GET_INDEX (*list, j);
-          GET_INDEX (*list, j) = GET_INDEX (*list, max_index);
-          GET_INDEX (*list, max_index) = temp;
-      
-      /* xx -- really have to sort?  */
-      LOG ("needed exchange");
-      at_exception_warning(exception, "needed exchange");
-        }
-    }
-
-  /* The list is sorted.  Now look for adjacent entries.  Each time
-     through the loop we insert the current entry and, if appropriate,
-     the next entry.  */
-  for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++)
-    {
-      unsigned current = GET_INDEX (*list, j);
-      unsigned next = GET_INDEX (*list, j + 1);
-
-      /* We should never have inserted the same element twice.  */
-      /* assert (current != next); */
-
-      if ((remove_adj_corners) && ((next == current + 1) || (next == current)))
-        j++;
-
-      append_index (&new_list, current);
-    }
-
-  /* Don't append the last element if it is 1) adjacent to the previous
-     one; or 2) adjacent to the very first one.  */
-  last = GET_LAST_INDEX (*list);
-  if (INDEX_LIST_LENGTH (new_list) == 0
-      || !(last == GET_LAST_INDEX (new_list) + 1
-           || (last == last_index && GET_INDEX (*list, 0) == 0)))
-    append_index (&new_list, last);
-
-  free_index_list (list);
-  *list = new_list;
-}
-
-/* A ``knee'' is a point which forms a ``right angle'' with its
-   predecessor and successor.  See the documentation (the `Removing
-   knees' section) for an example and more details.
-
-   The argument CLOCKWISE tells us which direction we're moving.  (We
-   can't figure that information out from just the single segment with
-   which we are given to work.)
-
-   We should never find two consecutive knees.
-
-   Since the first and last points are corners (unless the curve is
-   cyclic), it doesn't make sense to remove those.  */
-
-/* This evaluates to true if the vector V is zero in one direction and
-   nonzero in the other.  */
-#define ONLY_ONE_ZERO(v)                                                \
-  (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0))
-
-/* There are four possible cases for knees, one for each of the four
-   corners of a rectangle; and then the cases differ depending on which
-   direction we are going around the curve.  The tests are listed here
-   in the order of upper left, upper right, lower right, lower left.
-   Perhaps there is some simple pattern to the
-   clockwise/counterclockwise differences, but I don't see one.  */
-#define CLOCKWISE_KNEE(prev_delta, next_delta)                                                  \
-  ((prev_delta.dx == -1.0 && next_delta.dy == 1.0)                                              \
-   || (prev_delta.dy == 1.0 && next_delta.dx == 1.0)                                    \
-   || (prev_delta.dx == 1.0 && next_delta.dy == -1.0)                                   \
-   || (prev_delta.dy == -1.0 && next_delta.dx == -1.0))
-
-#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)                                   \
-  ((prev_delta.dy == 1.0 && next_delta.dx == -1.0)                                              \
-   || (prev_delta.dx == 1.0 && next_delta.dy == 1.0)                                    \
-   || (prev_delta.dy == -1.0 && next_delta.dx == 1.0)                                   \
-   || (prev_delta.dx == -1.0 && next_delta.dy == -1.0))
-
-
-
-static void
-remove_knee_points(curve_type const curve,
-                   bool       const clockwise) {
-
-      unsigned const offset = (CURVE_CYCLIC(curve) == true) ? 0 : 1;
-      curve_type const trimmed_curve = copy_most_of_curve(curve);
-
-      pm_pixelcoord previous;
-      unsigned i;
-
-      if (!CURVE_CYCLIC(curve))
-          append_pixel(trimmed_curve,
-                       real_to_int_coord(CURVE_POINT(curve, 0)));
-
-      previous = real_to_int_coord(CURVE_POINT(curve,
-                                               CURVE_PREV(curve, offset)));
-
-      for (i = offset; i < CURVE_LENGTH (curve) - offset; ++i) {
-          pm_pixelcoord const current =
-              real_to_int_coord(CURVE_POINT(curve, i));
-          pm_pixelcoord const next =
-              real_to_int_coord(CURVE_POINT(curve, CURVE_NEXT(curve, i)));
-          vector_type const prev_delta = IPsubtract(previous, current);
-          vector_type const next_delta = IPsubtract(next, current);
-
-          if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta)
-              && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta))
-                  || (!clockwise
-                      && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta))))
-              LOG2(" (%d,%d)", current.col, current.row);
-          else {
-              previous = current;
-              append_pixel(trimmed_curve, current);
-          }
-      }
-
-      if (!CURVE_CYCLIC(curve))
-          append_pixel(trimmed_curve,
-                       real_to_int_coord(LAST_CURVE_POINT(curve)));
-
-      if (CURVE_LENGTH(trimmed_curve) == CURVE_LENGTH(curve))
-          LOG(" (none)");
-
-      LOG(".\n");
-
-      free_curve(curve);
-      *curve = *trimmed_curve;
-      free(trimmed_curve);      /* free_curve? --- Masatake */
-}
-
-
-
-/* Smooth the curve by adding in neighboring points.  Do this
-   `filter_iterations' times.  But don't change the corners.  */
-
-static void
-filter (curve_type curve, fitting_opts_type *fitting_opts)
-{
-  unsigned iteration, this_point;
-  unsigned offset = (CURVE_CYCLIC (curve) == true) ? 0 : 1;
-  float_coord prev_new_point;
-
-  /* We must have at least three points---the previous one, the current
-     one, and the next one.  But if we don't have at least five, we will
-     probably collapse the curve down onto a single point, which means
-     we won't be able to fit it with a spline.  */
-  if (CURVE_LENGTH (curve) < 5)
-    {
-      LOG1 ("Length is %u, not enough to filter.\n", CURVE_LENGTH (curve));
-      return;
-    }
-
-  prev_new_point.x = FLT_MAX;
-  prev_new_point.y = FLT_MAX;
-  prev_new_point.z = FLT_MAX;
-
-  for (iteration = 0; iteration < fitting_opts->filter_iterations;
-   iteration++)
-    {
-      curve_type newcurve = copy_most_of_curve (curve);
-      bool collapsed = false;
-
-      /* Keep the first point on the curve.  */
-      if (offset)
-        append_point (newcurve, CURVE_POINT (curve, 0));
-
-      for (this_point = offset; this_point < CURVE_LENGTH (curve) - offset;
-           this_point++)
-        {
-          vector_type in, out, sum;
-          float_coord new_point;
-
-          /* Calculate the vectors in and out, computed by looking at n points
-             on either side of this_point. Experimental it was found that 2 is
-             optimal. */
-
-          signed int prev, prevprev; /* have to be signed */
-          unsigned int next, nextnext;
-          float_coord candidate = CURVE_POINT (curve, this_point);
-
-          prev = CURVE_PREV (curve, this_point);
-          prevprev = CURVE_PREV (curve, prev);
-          next = CURVE_NEXT (curve, this_point);
-          nextnext = CURVE_NEXT (curve, next);
-
-          /* Add up the differences from p of the `surround' points
-             before p.  */
-          in.dx = in.dy = in.dz = 0.0;
-
-          in = Vadd (in, Psubtract (CURVE_POINT (curve, prev), candidate));
-          if (prevprev >= 0)
-              in = Vadd (in, Psubtract (CURVE_POINT (curve, prevprev), candidate));
-
-          /* And the points after p.  Don't use more points after p than we
-             ended up with before it.  */
-          out.dx = out.dy = out.dz = 0.0;
-
-          out = Vadd (out, Psubtract (CURVE_POINT (curve, next), candidate));
-          if (nextnext < CURVE_LENGTH (curve))
-              out = Vadd (out, Psubtract (CURVE_POINT (curve, nextnext), candidate));
-
-          /* Start with the old point.  */
-          new_point = candidate;
-          sum = Vadd (in, out);
-          /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */
-          new_point.x += sum.dx / 6;
-          new_point.y += sum.dy / 6;
-          new_point.z += sum.dz / 6;
-          if (fabs (prev_new_point.x - new_point.x) < 0.3
-              && fabs (prev_new_point.y - new_point.y) < 0.3
-              && fabs (prev_new_point.z - new_point.z) < 0.3)
-            {
-              collapsed = true;
-              break;
-            }
-
-
-          /* Put the newly computed point into a separate curve, so it
-             doesn't affect future computation (on this iteration).  */
-          append_point (newcurve, prev_new_point = new_point);
-        }
-
-      if (collapsed)
-    free_curve (newcurve);
-      else
-    {
-          /* Just as with the first point, we have to keep the last point.  */
-          if (offset)
-        append_point (newcurve, LAST_CURVE_POINT (curve));
-      
-          /* Set the original curve to the newly filtered one, and go again.  */
-          free_curve (curve);
-          *curve = *newcurve;
-    }
-      free (newcurve);
-    }
-
-  log_curve (curve, false);
-}
-
-
-
-/* Find reasonable values for t for each point on CURVE.  The method is
-   called chord-length parameterization, which is described in Plass &
-   Stone.  The basic idea is just to use the distance from one point to
-   the next as the t value, normalized to produce values that increase
-   from zero for the first point to one for the last point.  */
-
-static void
-set_initial_parameter_values (curve_type curve)
-{
-  unsigned p;
-
-  LOG ("\nAssigning initial t values:\n  ");
-
-  CURVE_T (curve, 0) = 0.0;
-
-  for (p = 1; p < CURVE_LENGTH (curve); p++)
-    {
-      float_coord point = CURVE_POINT (curve, p),
-                           previous_p = CURVE_POINT (curve, p - 1);
-      float d = distance (point, previous_p);
-      CURVE_T (curve, p) = CURVE_T (curve, p - 1) + d;
-    }
-
-  assert (LAST_CURVE_T (curve) != 0.0);
-
-  for (p = 1; p < CURVE_LENGTH (curve); p++)
-    CURVE_T (curve, p) = CURVE_T (curve, p) / LAST_CURVE_T (curve);
-
-  log_entire_curve (curve);
-}
-
-/* Find an approximation to the tangent to an endpoint of CURVE (to the
-   first point if TO_START_POINT is true, else the last).  If
-   CROSS_CURVE is true, consider points on the adjacent curve to CURVE.
-
-   It is important to compute an accurate approximation, because the
-   control points that we eventually decide upon to fit the curve will
-   be placed on the half-lines defined by the tangents and
-   endpoints...and we never recompute the tangent after this.  */
-
-static void
-find_tangent (curve_type curve, bool to_start_point, bool cross_curve,
-  unsigned tangent_surround)
-{
-  vector_type tangent;
-  vector_type **curve_tangent = (to_start_point == true) ? &(CURVE_START_TANGENT (curve))
-                                               : &(CURVE_END_TANGENT (curve));
-  unsigned n_points = 0;
-
-  LOG1 ("  tangent to %s: ", (to_start_point == true) ? "start" : "end");
-
-  if (*curve_tangent == NULL)
-    {
-        MALLOCVAR_NOFAIL(*curve_tangent);
-      do
-        {
-          tangent = find_half_tangent (curve, to_start_point, &n_points,
-            tangent_surround);
-
-          if ((cross_curve == true) || (CURVE_CYCLIC (curve) == true))
-            {
-              curve_type adjacent_curve
-                = (to_start_point == true) ? PREVIOUS_CURVE (curve) : NEXT_CURVE (curve);
-              vector_type tangent2
-                = (to_start_point == false) ? find_half_tangent (adjacent_curve, true, &n_points,
-                tangent_surround) : find_half_tangent (adjacent_curve, true, &n_points,
-                tangent_surround);
-
-              LOG3 ("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ",
-                tangent2.dx, tangent2.dy, tangent2.dz);
-              tangent = Vadd (tangent, tangent2);
-            }
-          tangent_surround--;
-
-        }
-      while (tangent.dx == 0.0 && tangent.dy == 0.0);
-
-      assert (n_points > 0);
-      **curve_tangent = Vmult_scalar (tangent, (float)(1.0 / n_points));
-      if ((CURVE_CYCLIC (curve) == true) && CURVE_START_TANGENT (curve))
-          *CURVE_START_TANGENT (curve) = **curve_tangent;
-      if  ((CURVE_CYCLIC (curve) == true) && CURVE_END_TANGENT (curve))
-          *CURVE_END_TANGENT (curve) = **curve_tangent;
-    }
-  else
-    LOG ("(already computed) ");
-
-  LOG3 ("(%.3f,%.3f,%.3f).\n", (*curve_tangent)->dx, (*curve_tangent)->dy, (*curve_tangent)->dz);
-}
-
-/* Find the change in y and change in x for `tangent_surround' (a global)
-   points along CURVE.  Increment N_POINTS by the number of points we
-   actually look at.  */
-
-static vector_type
-find_half_tangent (curve_type c, bool to_start_point, unsigned *n_points,
-  unsigned tangent_surround)
-{
-  unsigned p;
-  int factor = to_start_point ? 1 : -1;
-  unsigned tangent_index = to_start_point ? 0 : c->length - 1;
-  float_coord tangent_point = CURVE_POINT (c, tangent_index);
-  vector_type tangent = { 0.0, 0.0 };
-  unsigned int surround;
-
-  if ((surround = CURVE_LENGTH (c) / 2) > tangent_surround)
-    surround = tangent_surround;
-
-  for (p = 1; p <= surround; p++)
-    {
-      int this_index = p * factor + tangent_index;
-      float_coord this_point;
-
-      if (this_index < 0 || this_index >= (int) c->length)
-        break;
-
-      this_point = CURVE_POINT (c, p * factor + tangent_index);
-
-      /* Perhaps we should weight the tangent from `this_point' by some
-         factor dependent on the distance from the tangent point.  */
-      tangent = Vadd (tangent,
-                      Vmult_scalar (Psubtract (this_point, tangent_point),
-                                    (float) factor));
-      (*n_points)++;
-    }
-
-  return tangent;
-}
-
-/* When this routine is called, we have computed a spline representation
-   for the digitized curve.  The question is, how good is it?  If the
-   fit is very good indeed, we might have an error of zero on each
-   point, and then WORST_POINT becomes irrelevant.  But normally, we
-   return the error at the worst point, and the index of that point in
-   WORST_POINT.  The error computation itself is the Euclidean distance
-   from the original curve CURVE to the fitted spline SPLINE.  */
-
-static float
-find_error (curve_type curve, spline_type spline, unsigned *worst_point,
-        at_exception_type * exception)
-{
-  unsigned this_point;
-  float total_error = 0.0;
-  float worst_error = FLT_MIN;
-
-  *worst_point = CURVE_LENGTH (curve) + 1;   /* A sentinel value.  */
-
-  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
-    {
-      float_coord curve_point = CURVE_POINT (curve, this_point);
-      float t = CURVE_T (curve, this_point);
-      float_coord spline_point = evaluate_spline (spline, t);
-      float this_error = distance (curve_point, spline_point);
-      if (this_error >= worst_error)
-        {
-         *worst_point = this_point;
-          worst_error = this_error;
-        }
-      total_error += this_error;
-    }
-
-  if (*worst_point == CURVE_LENGTH (curve) + 1)
-    { /* Didn't have any ``worst point''; the error should be zero.  */
-      *worst_point = 0;
-      if (epsilon_equal (total_error, 0.0))
-        LOG ("  Every point fit perfectly.\n");
-      else
-    {
-      LOG("No worst point found; something is wrong");
-      at_exception_warning(exception, "No worst point found; something is wrong");
-    }
-    }
-  else
-    {
-      if (epsilon_equal (total_error, 0.0))
-        LOG ("  Every point fit perfectly.\n");
-      else
-        {
-          LOG5 ("  Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n",
-              CURVE_POINT (curve, *worst_point).x,
-              CURVE_POINT (curve, *worst_point).y,
-              CURVE_POINT (curve, *worst_point).z, *worst_point, worst_error);
-          LOG1 ("  Total error was %.3f.\n", total_error);
-          LOG2 ("  Average error (over %u points) was %.3f.\n",
-              CURVE_LENGTH (curve), total_error / CURVE_LENGTH (curve));
-        }
-    }
-
-  return worst_error;
-}
-
-
-/* Lists of array indices (well, that is what we use it for).  */
-
-static index_list_type
-new_index_list (void)
-{
-  index_list_type index_list;
-
-  index_list.data = NULL;
-  INDEX_LIST_LENGTH (index_list) = 0;
-
-  return index_list;
-}
-
-static void
-free_index_list (index_list_type *index_list)
-{
-  if (INDEX_LIST_LENGTH (*index_list) > 0)
-    {
-      free (index_list->data);
-      index_list->data = NULL;
-      INDEX_LIST_LENGTH (*index_list) = 0;
-    }
-}
-
-static void
-append_index (index_list_type *list, unsigned new_index)
-{
-  INDEX_LIST_LENGTH (*list)++;
-  REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list));
-  list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index;
-}
-
-
-/* Return the Euclidean distance between P1 and P2.  */
-
-static float
-distance (float_coord p1, float_coord p2)
-{
-  float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z;
-  return (float) sqrt (SQR(x) + SQR(y) + SQR(z));
-}
diff --git a/converter/other/pamtosvg/pamtosvg.c b/converter/other/pamtosvg/pamtosvg.c
index 7262204c..dbe67c74 100644
--- a/converter/other/pamtosvg/pamtosvg.c
+++ b/converter/other/pamtosvg/pamtosvg.c
@@ -4,6 +4,7 @@
 #include <assert.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
@@ -211,6 +212,7 @@ parseCommandLine(int argc,
             pm_error("Too many arguments (%u).  The only non-option argument "
                      "is the input file name.", argc-1);
     }
+    free(option_def);
 }
 
 
@@ -389,7 +391,7 @@ main(int argc, char * argv[]) {
     pm_close(ifP);
     if (cmdline.log)
         pm_close(log_file);
-    
+
     at_splines_free(splinesP);
     at_bitmap_free(bitmapP);
 
diff --git a/converter/other/pamtosvg/pamtosvg.test b/converter/other/pamtosvg/pamtosvg.test
index df3a07d3..3217287e 100644
--- a/converter/other/pamtosvg/pamtosvg.test
+++ b/converter/other/pamtosvg/pamtosvg.test
@@ -1,6 +1,8 @@
+echo "Test 1.  Should print nothing"
 # This will print nothing if successful (diff will find no difference)
-ppmmake black 20 20 | ppmdraw -script="line 5 2 15 17" | pamtosvg | \
+ppmmake black 20 20 | ppmdraw -script="line 5 2 15 17" | ./pamtosvg | \
   diff testline.svg -
 
+echo "Test 2.  Should print nothing"
 # This will print nothing if successful (diff will find no difference)
-pamtosvg ../../../../testgrid.pbm | diff testgrid.svg -
+./pamtosvg ../../../testgrid.pbm | diff testgrid.svg -
diff --git a/converter/other/pamtosvg/pxl-outline.c b/converter/other/pamtosvg/pxl-outline.c
index a55a8b95..a1fa5299 100644
--- a/converter/other/pamtosvg/pxl-outline.c
+++ b/converter/other/pamtosvg/pxl-outline.c
@@ -83,8 +83,9 @@ getBitmapColor(bitmap_type  const bitmap,
                unsigned int const row,
                unsigned int const col) {
 
+    unsigned char * const p = BITMAP_PIXEL(bitmap, row, col);
+
     pixel pix;
-    unsigned char *p = BITMAP_PIXEL (bitmap, row, col);
   
     if (bitmap.np >= 3)
         PPM_ASSIGN(pix, p[0], p[1], p[2]);
@@ -248,19 +249,19 @@ next_unmarked_pixel(unsigned int *   const row,
 
 
 static pixel_outline_type
-find_one_centerline(bitmap_type    const bitmap,
-                    direction_type const original_dir,
-                    unsigned int   const original_row,
-                    unsigned int   const original_col,
-                    bitmap_type *  const marked) {
+findOneCenterline(bitmap_type    const bitmap,
+                  direction_type const originalDir,
+                  unsigned int   const originalRow,
+                  unsigned int   const originalCol,
+                  bitmap_type *  const marked) {
 
-    direction_type search_dir;
+    direction_type searchDir;
     unsigned int row, col;
     pixel_outline_type outline;
 
     outline = new_pixel_outline();
     outline.open  = false;
-    outline.color = getBitmapColor(bitmap, original_row, original_col);
+    outline.color = getBitmapColor(bitmap, originalRow, originalCol);
 
     /* Add the starting pixel to the output list, changing from bitmap
        to Cartesian coordinates and specifying the left edge so that
@@ -268,22 +269,22 @@ find_one_centerline(bitmap_type    const bitmap,
     */
     {
         pm_pixelcoord pos;
-        pos.col = original_col; pos.row = bitmap.height - original_row - 1;
+        pos.col = originalCol; pos.row = bitmap.height - originalRow - 1;
         LOG2(" (%d,%d)", pos.col, pos.row);
         append_outline_pixel(&outline, pos);
     }
-    search_dir = original_dir;  /* initial value */
-    row = original_row;         /* initial value */
-    col = original_col;         /* initial values */
+    searchDir = originalDir;  /* initial value */
+    row = originalRow;         /* initial value */
+    col = originalCol;         /* initial values */
 
     for ( ; ; ) {
-        unsigned int const prev_row = row;
-        unsigned int const prev_col = col;
+        unsigned int const prevRow = row;
+        unsigned int const prevCol = col;
 
         /* If there is no adjacent, unmarked pixel, we can't proceed
            any further, so return an open outline.
         */
-        if (!next_unmarked_pixel(&row, &col, &search_dir, bitmap, marked)) {
+        if (!next_unmarked_pixel(&row, &col, &searchDir, bitmap, marked)) {
             outline.open = true;
             break;
         }
@@ -291,12 +292,12 @@ find_one_centerline(bitmap_type    const bitmap,
         /* If we've moved to a new pixel, mark all edges of the previous
            pixel so that it won't be revisited.
         */
-        if (!(prev_row == original_row && prev_col == original_col))
-            mark_dir(prev_row, prev_col, search_dir, marked);
-        mark_dir(row, col, (search_dir + 4) % 8, marked);
+        if (!(prevRow == originalRow && prevCol == originalCol))
+            mark_dir(prevRow, prevCol, searchDir, marked);
+        mark_dir(row, col, (searchDir + 4) % 8, marked);
 
         /* If we've returned to the starting pixel, we're done. */
-        if (row == original_row && col == original_col)
+        if (row == originalRow && col == originalCol)
             break;
 
         
@@ -308,7 +309,7 @@ find_one_centerline(bitmap_type    const bitmap,
             append_outline_pixel(&outline, pos);
         }
     }
-    mark_dir(original_row, original_col, original_dir, marked);
+    mark_dir(originalRow, originalCol, originalDir, marked);
 
     return outline;
 }
@@ -392,7 +393,7 @@ find_centerline_pixels(bitmap_type         const bitmap,
           LOG2("#%u: (%sclockwise) ", O_LIST_LENGTH(outline_list),
                clockwise ? "" : "counter");
 
-          outline = find_one_centerline(bitmap, dir, row, col, &marked);
+          outline = findOneCenterline(bitmap, dir, row, col, &marked);
 
           /* If the outline is open (i.e., we didn't return to the
              starting pixel), search from the starting pixel in the
@@ -450,7 +451,7 @@ find_centerline_pixels(bitmap_type         const bitmap,
               }
               if (okay) {
                   partial_outline =
-                      find_one_centerline(bitmap, dir, row, col, &marked);
+                      findOneCenterline(bitmap, dir, row, col, &marked);
                   concat_pixel_outline(&outline, &partial_outline);
                   if (partial_outline.data)
                       free(partial_outline.data);
diff --git a/converter/other/pamtosvg/spline.c b/converter/other/pamtosvg/spline.c
index 5bdf0c0e..61167ec4 100644
--- a/converter/other/pamtosvg/spline.c
+++ b/converter/other/pamtosvg/spline.c
@@ -105,11 +105,13 @@ new_spline_list_with_spline (spline_type spline)
    elements, since they are arrays in automatic storage.  And we don't
    want to free the list if it was empty.  */
 
+
+
 void
-free_spline_list (spline_list_type spline_list)
-{
-  if (SPLINE_LIST_DATA (spline_list) != NULL)
-    free (SPLINE_LIST_DATA (spline_list));
+free_spline_list(spline_list_type spline_list) {
+
+    if (SPLINE_LIST_DATA(spline_list) != NULL)
+        free(SPLINE_LIST_DATA(spline_list));
 }
 
 
diff --git a/converter/other/pamtosvg/spline.h b/converter/other/pamtosvg/spline.h
index 05a56e23..96ceab4e 100644
--- a/converter/other/pamtosvg/spline.h
+++ b/converter/other/pamtosvg/spline.h
@@ -21,6 +21,7 @@ typedef at_spline_type spline_type;
 #define START_POINT(spl)        ((spl).v[0])
 #define CONTROL1(spl)           ((spl).v[1])
 #define CONTROL2(spl)           ((spl).v[2])
+#define BEG_POINT(spl)          ((spl).v[0])
 #define END_POINT(spl)          ((spl).v[3])
 #define SPLINE_DEGREE(spl)      ((spl).degree)
 #define SPLINE_LINEARITY(spl)   ((spl).linearity)
diff --git a/converter/other/pamtosvg/vector.c b/converter/other/pamtosvg/vector.c
index 18d6de59..7678f73a 100644
--- a/converter/other/pamtosvg/vector.c
+++ b/converter/other/pamtosvg/vector.c
@@ -17,137 +17,150 @@ static float acos_d (float, at_exception_type * excep);
 /* Given the point COORD, return the corresponding vector.  */
 
 vector_type
-make_vector (const float_coord c)
-{
-  vector_type v;
+make_vector(float_coord const c) {
 
-  v.dx = c.x;
-  v.dy = c.y;
-  v.dz = c.z;
+    vector_type v;
+
+    v.dx = c.x;
+    v.dy = c.y;
+    v.dz = c.z;
 
-  return v;
+    return v;
 }
 
 
+
 /* And the converse: given a vector, return the corresponding point.  */
 
 float_coord
-vector_to_point (const vector_type v)
-{
-  float_coord coord;
+vector_to_point(vector_type const v) {
+
+    float_coord coord;
 
-  coord.x = v.dx;
-  coord.y = v.dy;
-  coord.z = v.dz;
+    coord.x = v.dx;
+    coord.y = v.dy;
+    coord.z = v.dz;
 
-  return coord;
+    return coord;
 }
 
 
-float
-magnitude (const vector_type v)
-{
-  return (float) sqrt (v.dx * v.dx + v.dy * v.dy + v.dz * v.dz);
-}
 
+float
+magnitude(vector_type const v) {
 
-vector_type
-normalize (const vector_type v)
-{
-  vector_type new_v;
-  float m = magnitude (v);
+    return sqrt(SQR(v.dx) + SQR(v.dy) + SQR(v.dz));
+}
 
-  /* assert (m > 0.0); */
 
-  if (m > 0.0)
-  {
-    new_v.dx = v.dx / m;
-    new_v.dy = v.dy / m;
-    new_v.dz = v.dz / m;
-  }
-  else
-  {
-	new_v.dx = v.dx;
-    new_v.dy = v.dy;
-    new_v.dz = v.dz;
-  }
 
-  return new_v;
+vector_type
+normalize(vector_type const v) {
+
+    vector_type new_v;
+    float const m = magnitude(v);
+
+    if (m > 0.0) {
+        new_v.dx = v.dx / m;
+        new_v.dy = v.dy / m;
+        new_v.dz = v.dz / m;
+    } else {
+        new_v.dx = v.dx;
+        new_v.dy = v.dy;
+        new_v.dz = v.dz;
+    }
+    
+    return new_v;
 }
 
 
+
 vector_type
-Vadd (const vector_type v1, const vector_type v2)
-{
-  vector_type new_v;
+Vadd(vector_type const v1,
+     vector_type const v2) {
+
+    vector_type new_v;
 
-  new_v.dx = v1.dx + v2.dx;
-  new_v.dy = v1.dy + v2.dy;
-  new_v.dz = v1.dz + v2.dz;
+    new_v.dx = v1.dx + v2.dx;
+    new_v.dy = v1.dy + v2.dy;
+    new_v.dz = v1.dz + v2.dz;
 
-  return new_v;
+    return new_v;
 }
 
 
+
 float
-Vdot (const vector_type v1, const vector_type v2)
-{
-  return v1.dx * v2.dx + v1.dy * v2.dy + v1.dz * v2.dz;
+Vdot(vector_type const v1,
+     vector_type const v2) {
+
+    return v1.dx * v2.dx + v1.dy * v2.dy + v1.dz * v2.dz;
 }
 
 
+
 vector_type
-Vmult_scalar (const vector_type v, const float r)
-{
-  vector_type new_v;
+Vmult_scalar(vector_type const v,
+             float       const r) {
 
-  new_v.dx = v.dx * r;
-  new_v.dy = v.dy * r;
-  new_v.dz = v.dz * r;
+    vector_type new_v;
 
-  return new_v;
+    new_v.dx = v.dx * r;
+    new_v.dy = v.dy * r;
+    new_v.dz = v.dz * r;
+
+    return new_v;
 }
 
 
+
 /* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in
-   degrees, in the range zero to 180.  */
+   degrees, in the range zero to 180.
+*/
 
 float
-Vangle (const vector_type in_vector, 
-	const vector_type out_vector,
-	at_exception_type * exp)
-{
-  vector_type v1 = normalize (in_vector);
-  vector_type v2 = normalize (out_vector);
+Vangle(vector_type         const in_vector, 
+       vector_type         const out_vector,
+       at_exception_type * const exP) {
+
+    vector_type const v1 = normalize(in_vector);
+    vector_type const v2 = normalize(out_vector);
 
-  return acos_d (Vdot (v2, v1), exp);
+    return acos_d(Vdot(v2, v1), exP);
 }
 
 
+
 float_coord
-Vadd_point (const float_coord c, const vector_type v)
-{
-  float_coord new_c;
+Vadd_point(float_coord const c,
+           vector_type const v) {
+
+    float_coord new_c;
 
-  new_c.x = c.x + v.dx;
-  new_c.y = c.y + v.dy;
-  new_c.z = c.z + v.dz;
-  return new_c;
+    new_c.x = c.x + v.dx;
+    new_c.y = c.y + v.dy;
+    new_c.z = c.z + v.dz;
+
+    return new_c;
 }
 
 
+
 float_coord
-Vsubtract_point (const float_coord c, const vector_type v)
-{
-  float_coord new_c;
+Vsubtract_point(float_coord const c,
+                vector_type const v) {
+
+    float_coord new_c;
 
-  new_c.x = c.x - v.dx;
-  new_c.y = c.y - v.dy;
-  new_c.z = c.z - v.dz;
-  return new_c;
+    new_c.x = c.x - v.dx;
+    new_c.y = c.y - v.dy;
+    new_c.z = c.z - v.dz;
+
+    return new_c;
 }
 
 
+
 pm_pixelcoord
 Vadd_int_point(pm_pixelcoord const c,
                vector_type   const v) {
@@ -161,56 +174,73 @@ Vadd_int_point(pm_pixelcoord const c,
 }
 
 
+
 vector_type
-Vabs (const vector_type v)
-{
-  vector_type new_v;
+Vabs(vector_type const v) {
 
-  new_v.dx = (float) fabs (v.dx);
-  new_v.dy = (float) fabs (v.dy);
-  new_v.dz = (float) fabs (v.dz);
-  return new_v;
+    vector_type new_v;
+
+    new_v.dx = (float) fabs (v.dx);
+    new_v.dy = (float) fabs (v.dy);
+    new_v.dz = (float) fabs (v.dz);
+
+    return new_v;
 }
 
 
+
 /* Operations on points.  */
 
 float_coord
-Padd (const float_coord coord1, const float_coord coord2)
-{
-  float_coord sum;
+Padd(float_coord const coord1,
+     float_coord const coord2) {
 
-  sum.x = coord1.x + coord2.x;
-  sum.y = coord1.y + coord2.y;
-  sum.z = coord1.z + coord2.z;
+    float_coord sum;
 
-  return sum;
+    sum.x = coord1.x + coord2.x;
+    sum.y = coord1.y + coord2.y;
+    sum.z = coord1.z + coord2.z;
+
+    return sum;
 }
 
 
+
 float_coord
-Pmult_scalar (const float_coord coord, const float r)
-{
-  float_coord answer;
+Pmult_scalar(float_coord const coord,
+             float       const r) {
+
+    float_coord answer;
 
-  answer.x = coord.x * r;
-  answer.y = coord.y * r;
-  answer.z = coord.z * r;
+    answer.x = coord.x * r;
+    answer.y = coord.y * r;
+    answer.z = coord.z * r;
 
-  return answer;
+    return answer;
 }
 
 
+
 vector_type
-Psubtract (const float_coord c1, const float_coord c2)
-{
-  vector_type v;
+Psubtract(float_coord const c1,
+          float_coord const c2) {
+
+    vector_type v;
 
-  v.dx = c1.x - c2.x;
-  v.dy = c1.y - c2.y;
-  v.dz = c1.z - c2.z;
+    v.dx = c1.x - c2.x;
+    v.dy = c1.y - c2.y;
+    v.dz = c1.z - c2.z;
 
-  return v;
+    return v;
+}
+
+
+
+vector_type
+Pdirection(float_coord const final,
+           float_coord const initial) {
+
+    return normalize(Psubtract(final, initial));
 }
 
 
@@ -233,23 +263,27 @@ IPsubtract(pm_pixelcoord const coord1,
 
 
 static float
-acos_d (float v, at_exception_type * excep)
-{
-  float a;
-
-  if (epsilon_equal (v, 1.0))
-    v = 1.0;
-  else if (epsilon_equal (v, -1.0))
-    v = -1.0;
-
-  errno = 0;
-  a = (float) acos (v);
-  if (errno == ERANGE || errno == EDOM)
-    {
-      at_exception_fatal(excep, strerror(errno));
-      return 0.0;
-    }
-  
-  
-  return a * (float) 180.0 / (float) M_PI;
+acos_d(float               const v,
+       at_exception_type * const excepP) {
+
+    float vAdj;
+    float a;
+    float retval;
+
+    if (epsilon_equal(v, 1.0))
+        vAdj = 1.0;
+    else if (epsilon_equal(v, -1.0))
+        vAdj = -1.0;
+    else
+        vAdj = v;
+
+    errno = 0;
+    a = acos(vAdj);
+    if (errno == ERANGE || errno == EDOM) {
+        at_exception_fatal(excepP, strerror(errno));
+        retval = 0.0;
+    } else
+        retval = a * 180.0 / M_PI;
+
+    return retval;
 }
diff --git a/converter/other/pamtosvg/vector.h b/converter/other/pamtosvg/vector.h
index 74fb2fd2..b5c85c38 100644
--- a/converter/other/pamtosvg/vector.h
+++ b/converter/other/pamtosvg/vector.h
@@ -15,32 +15,58 @@ typedef struct
 
 
 /* Consider a point as a vector from the origin.  */
-extern vector_type make_vector (const float_coord);
+vector_type
+make_vector(float_coord const);
 
 /* And a vector as a point, i.e., a displacement from the origin.  */
-extern float_coord vector_to_point (const vector_type);
+float_coord
+vector_to_point(vector_type const);
 
 
 /* Definitions for these common operations can be found in any decent
    linear algebra book, and most calculus books.  */
 
-extern float magnitude (const vector_type);
-extern vector_type normalize (const vector_type);
+float
+magnitude(vector_type const);
+
+vector_type
+normalize(vector_type const);
+
+vector_type
+Vadd(vector_type const,
+     vector_type const);
 
-extern vector_type Vadd (const vector_type, const vector_type);
-extern float Vdot (const vector_type, const vector_type);
-extern vector_type Vmult_scalar (const vector_type, const float);
-extern float Vangle (const vector_type in, const vector_type out, at_exception_type * exp);
+float
+Vdot(vector_type const,
+     vector_type const);
+
+vector_type
+Vmult_scalar(vector_type const,
+             float       const);
+
+float
+Vangle(vector_type         const in,
+       vector_type         const out,
+       at_exception_type * const exP);
 
 /* These operations could have been named `P..._vector' just as well as
    V..._point, so we may as well allow both names.  */
+
 #define Padd_vector Vadd_point
-extern float_coord Vadd_point
-  (const float_coord, const vector_type);
+
+float_coord
+Vadd_point(float_coord const,
+           vector_type const);
 
 #define Psubtract_vector Vsubtract_point
-extern float_coord Vsubtract_point
-  (const float_coord, const vector_type);
+
+float_coord
+Vsubtract_point(float_coord const,
+                vector_type const);
+
+vector_type
+Pdirection(float_coord const final,
+           float_coord const initial);
 
 /* This returns the rounded sum.  */
 #define IPadd_vector Vadd_int_point
@@ -50,22 +76,28 @@ Vadd_int_point(pm_pixelcoord const c,
                vector_type   const v);
 
 /* Take the absolute value of both components.  */
-extern vector_type Vabs (const vector_type);
+vector_type
+Vabs(vector_type const);
 
 /* Operations on points with real coordinates.  It is not orthogonal,
    but more convenient, to have the subtraction operator return a
    vector, and the addition operator return a point.  */
-extern vector_type Psubtract
-  (const float_coord, const float_coord);
+vector_type
+Psubtract(float_coord const,
+          float_coord const);
 
 vector_type
 IPsubtract(pm_pixelcoord const coord1,
            pm_pixelcoord const coord2);
 
 /* These are heavily used in spline fitting.  */
-extern float_coord Padd (const float_coord,
-                                  const float_coord);
-extern float_coord Pmult_scalar (const float_coord, const float);
 
-#endif
+float_coord
+Padd(float_coord const,
+     float_coord const);
 
+float_coord
+Pmult_scalar(float_coord const,
+             float       const);
+
+#endif
diff --git a/converter/other/pamtotga.c b/converter/other/pamtotga.c
index 6c8769ed..c23b92b1 100644
--- a/converter/other/pamtotga.c
+++ b/converter/other/pamtotga.c
@@ -15,6 +15,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pammap.h"
 #include "shhopt.h"
@@ -270,7 +271,7 @@ computeRunlengths(struct pam * const pamP,
 
 static void
 computeOutName(struct cmdlineInfo const cmdline, 
-               const char ** const outNameP) {
+               const char **      const outNameP) {
     
     char * workarea;
 
diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c
index 62315f9b..1b31c65b 100644
--- a/converter/other/pamtotiff.c
+++ b/converter/other/pamtotiff.c
@@ -788,16 +788,6 @@ createTiffGenerator(int          const ofd,
 
     const char * option;
 
-    /* Before 10.12 (November 2002), we set O_NONBLOCK here:
-
-       fcntl( 1, F_SETFL, O_NONBLOCK ) ; 
-   
-       I have no idea why.  The comment attached said, 
-
-         acooke dec99 - otherwise blocks on read inside 
-         next line (Linux i386) 
-    */
-
     validateSeekableOutputFile(ofd, outFileName);
 
     if (append)
@@ -1031,6 +1021,8 @@ validateReadableStdout(void) {
   error message about a file I/O error.  We, on the other hand, produce
   a helpful error message.
 -----------------------------------------------------------------------------*/
+#if !defined(WIN32) || defined(__CYGWIN__)
+
     int flags;
 
     flags = fcntl(STDOUT_FILENO, F_GETFL);
@@ -1045,6 +1037,7 @@ validateReadableStdout(void) {
                      "In order to create a multi-image TIFF stream, "
                      "Standard Output must be both readable and writable.");
     }
+#endif
 }
 
 
diff --git a/converter/other/pamtouil.c b/converter/other/pamtouil.c
index b9972abd..1a53be0f 100644
--- a/converter/other/pamtouil.c
+++ b/converter/other/pamtouil.c
@@ -17,6 +17,8 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #include <ctype.h>
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pammap.h"
 #include "colorname.h"
@@ -34,18 +36,18 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input files */
-    char *outname;         /* output filename, less "_icon" */
+    const char * inputFilespec;  /* Filespecs of input files */
+    char * outname;         /* output filename, less "_icon" */
     unsigned int verbose;
 };
 
 
 
 typedef struct {    /* character-pixel mapping */
-    const char* cixel;    /* character string printed for pixel */
-    const char* rgbname;  /* ascii rgb color, either mnemonic or #rgb value */
-    const char* uilname;  /* same, with spaces replaced by underbars */
-    bool        transparent;
+    const char * cixel;    /* character string printed for pixel */
+    const char * rgbname;  /* ascii rgb color, either mnemonic or #rgb value */
+    const char * uilname;  /* same, with spaces replaced by underbars */
+    bool         transparent;
 } cixel_map;
 
 
@@ -95,10 +97,10 @@ parseCommandLine(int argc, char ** argv,
 
         /* Remove trailing "_icon" */
         barPos = strrchr(cmdlineP->outname, '_');
-        if (barPos && STREQ(barPos, "_icon")) 
+        if (barPos && streq(barPos, "_icon")) 
             *barPos = '\0';
     } else {
-        if (STREQ(cmdlineP->inputFilespec, "-"))
+        if (streq(cmdlineP->inputFilespec, "-"))
             cmdlineP->outname = strdup("noname");
         else {
             char * dotPos;
@@ -154,7 +156,8 @@ genNumstr(int  const number,
 
 
 static const char * 
-uilName(const char * const rgbname, bool const transparent) {
+uilName(const char * const rgbname,
+        bool         const transparent) {
 /*----------------------------------------------------------------------------
    Return a string in newly malloc'ed storage which is an appropriate 
    color name for the UIL palette.  It is the same as the rgb name,
@@ -200,8 +203,8 @@ genCmap(struct pam *   const pamP,
     unsigned int colorIndex;
     {
         /* Figure out how many characters per pixel we'll be using.
-        ** Don't want to be forced to link with libm.a, so using a
-        ** division loop rather than a log function.  
+           Don't want to be forced to link with libm.a, so using a
+           division loop rather than a log function.  
         */
         unsigned int i;
         for (*charsppP = 0, i = ncolors; i > 0; ++(*charsppP))
@@ -209,7 +212,7 @@ genCmap(struct pam *   const pamP,
     }
 
     /* Generate the character-pixel string and the rgb name for each colormap
-    ** entry.
+       entry.
     */
     for (colorIndex = 0; colorIndex < ncolors; ++colorIndex) {
         bool nameAlreadyInCmap;
@@ -237,7 +240,7 @@ genCmap(struct pam *   const pamP,
         nameAlreadyInCmap = FALSE;   /* initial assumption */
         for (j = 0; j < colorIndex; ++j) {
             if (cmap[j].rgbname != NULL && 
-                STREQ(colorname, cmap[j].rgbname) &&
+                streq(colorname, cmap[j].rgbname) &&
                 cmap[j].transparent == transparent) {
                 nameAlreadyInCmap = TRUE;
                 indexOfName = j;
@@ -247,12 +250,12 @@ genCmap(struct pam *   const pamP,
             /* Make the entry a cross-reference to the earlier entry */
             cmap[colorIndex].uilname = NULL;
             cmap[colorIndex].rgbname = NULL;
-            cmap[colorIndex].cixel = cmap[indexOfName].cixel;
+            cmap[colorIndex].cixel   = cmap[indexOfName].cixel;
         } else {
             cmap[colorIndex].uilname = uilName(colorname, transparent);
             cmap[colorIndex].rgbname = strdup(colorname);
             if (cmap[colorIndex].rgbname == NULL)
-                pm_error( "out of memory allocating color name" );
+                pm_error("out of memory allocating color name");
             
             cmap[colorIndex].transparent = transparent;
             
@@ -329,15 +332,16 @@ writeRaster(struct pam *  const pamP,
             unsigned int  const ncolors,
             tuplehash     const cht, 
             unsigned int  const charspp) {
-    
-    int row;
-    /* Write out the ascii character-pixel image. */
+/*----------------------------------------------------------------------------
+   Write out the ascii character-pixel image.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
 
     printf("\n");
     printf("%s_icon : exported icon( color_table = %s_rgb,\n",
            outname, outname);
     for (row = 0; row < pamP->height; ++row) {
-        int col;
+        unsigned int col;
 
         printf("    '");
         for (col = 0; col < pamP->width; ++col) {
@@ -360,27 +364,19 @@ writeRaster(struct pam *  const pamP,
 }
 
 
-static void
-freeString(const char * const s) {
-/*----------------------------------------------------------------------------
-   This is just free(), but with type checking for const char *.
------------------------------------------------------------------------------*/
-    free((void *)s);
-}
-
-
 
 static void
-freeCmap(cixel_map cmap[], unsigned int const ncolors) {
+freeCmap(cixel_map    const cmap[],
+         unsigned int const ncolors) {
 
-    int i;
+    unsigned int i;
 
     for (i = 0; i < ncolors; ++i) {
         cixel_map const cmapEntry = cmap[i];
         if (cmapEntry.uilname)
-            freeString(cmapEntry.uilname);
+            strfree(cmapEntry.uilname);
         if (cmapEntry.rgbname)
-            freeString(cmapEntry.rgbname);
+            strfree(cmapEntry.rgbname);
     }
 }
 
@@ -391,8 +387,8 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     struct pam pam;   /* Input PAM image */
-    FILE* ifP;
-    tuple** tuples;
+    FILE * ifP;
+    tuple ** tuples;
     unsigned int ncolors;
     tuplehash cht;
     tupletable chv;
diff --git a/converter/other/pamtoxvmini.c b/converter/other/pamtoxvmini.c
index 3207b0d5..e1aa9b52 100644
--- a/converter/other/pamtoxvmini.c
+++ b/converter/other/pamtoxvmini.c
@@ -67,7 +67,6 @@ makeXvPalette(xvPalette * const xvPaletteP) {
             }
         }
     }
-
 }
 
 
@@ -237,7 +236,7 @@ main(int    argc,
     struct pam pam;
     xvPalette xvPalette;
  
-    ppm_init(&argc, argv);
+    pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/converter/other/pfmtopam.c b/converter/other/pfmtopam.c
index 9430f5fa..1617ed31 100644
--- a/converter/other/pfmtopam.c
+++ b/converter/other/pfmtopam.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pm_gamma.h"
 #include "shhopt.h"
diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c
index 98bdc332..f828b716 100644
--- a/converter/other/pgmtopbm.c
+++ b/converter/other/pgmtopbm.c
@@ -11,10 +11,12 @@
 */
 
 #include <assert.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
 #include "pgm.h"
 #include "dithers.h"
 #include "mallocvar.h"
-#include "shhopt.h"
 
 enum halftone {QT_FS, QT_THRESH, QT_DITHER8, QT_CLUSTER, QT_HILBERT};
 
@@ -346,6 +348,10 @@ struct converter {
 
 
 
+/*=============================================================================
+                 Converter: fs
+=============================================================================*/
+
 unsigned int const fs_scale      = 1024;
 unsigned int const half_fs_scale = 512;
 
@@ -445,7 +451,7 @@ createFsConverter(unsigned int const cols,
     /* Initialize Floyd-Steinberg error vectors. */
     MALLOCARRAY_NOFAIL(stateP->thiserr, cols + 2);
     MALLOCARRAY_NOFAIL(stateP->nexterr, cols + 2);
-    srand((int)(time(NULL) ^ getpid()));
+    srand(pm_randseed());
 
     {
         /* (random errors in [-fs_scale/8 .. fs_scale/8]) */
@@ -468,6 +474,10 @@ createFsConverter(unsigned int const cols,
 
 
 
+/*=============================================================================
+                 Converter: thresh
+=============================================================================*/
+
 struct threshState {
     gray threshval;
 };
@@ -521,16 +531,28 @@ createThreshConverter(unsigned int const cols,
 
 
 
+/*=============================================================================
+                 Converter: dither8
+=============================================================================*/
+
+struct dither8State {
+    int dither8[16][16];
+};
+
+
+
 static void
 dither8ConvertRow(struct converter * const converterP,
                   unsigned int       const row,
                   gray                     grayrow[],
                   bit                      bitrow[]) {
 
+    struct dither8State * const stateP = converterP->stateP;
+
     unsigned int col;
 
     for (col = 0; col < converterP->cols; ++col)
-        if (grayrow[col] > dither8[row % 16][col % 16])
+        if (grayrow[col] > stateP->dither8[row % 16][col % 16])
             bitrow[col] = PBM_WHITE;
         else
             bitrow[col] = PBM_BLACK;
@@ -538,29 +560,47 @@ dither8ConvertRow(struct converter * const converterP,
 
 
 
+static void
+dither8Destroy(struct converter * const converterP) {
+
+    struct dither8State * const stateP = converterP->stateP;
+
+    free(stateP);
+}
+
+
+
 static struct converter
 createDither8Converter(unsigned int const cols, 
                        gray         const maxval) {
 
     struct converter converter;
+    struct dither8State * stateP;
 
     unsigned int row;
 
+    MALLOCVAR_NOFAIL(stateP);
+
     converter.cols = cols;
     converter.convertRow = &dither8ConvertRow;
-    converter.destroy = NULL;
+    converter.destroy = dither8Destroy;
+    converter.stateP = stateP;
 
     /* Scale dither matrix. */
     for (row = 0; row < 16; ++row) {
         unsigned int col;
         for (col = 0; col < 16; ++col)
-            dither8[row][col] = dither8[row][col] * maxval / 256;
+            stateP->dither8[row][col] = dither8[row][col] * maxval / 256;
     }
     return converter;
 }
 
 
 
+/*=============================================================================
+                 Converter: cluster
+=============================================================================*/
+
 struct clusterState {
     unsigned int radius;
     int ** clusterMatrix;
diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c
index 7693fe0a..7194db49 100644
--- a/converter/other/pgmtoppm.c
+++ b/converter/other/pgmtoppm.c
@@ -10,147 +10,232 @@
 ** implied warranty.
 */
 
+#define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "ppm.h"
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    gray* grayrow;
-    register gray* gP;
-    pixel p;
-    pixel* pixelrow;
-    register pixel* pP;
-    pixel** mappixels;
-    int argn, rows, cols, format, maprows, mapcols, mapmaxcolor, row;
-    register int col;
-    gray maxval;
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilename;  /* '-' if stdin */
+    const char * map;
+    const char * colorBlack;
+    const char * colorWhite;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int mapSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "map",            OPT_STRING,    &cmdlineP->map,
+            &mapSpec,            0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!mapSpec)
+        cmdlineP->map = NULL;
+
+    if (mapSpec) {
+        /* No color argument; only argument is file name */
+        if (argc-1 < 1)
+            cmdlineP->inputFilename = "-";
+        else {
+            cmdlineP->inputFilename = argv[1];
+            if (argc-1 > 1)
+                pm_error("With -map option, there is at most one argument: "
+                         "the file name.  You specified %u", argc-1);
+        }
+    } else {
+        /* Arguments are color or color range and file name */
+        if (argc-1 < 1) {
+            cmdlineP->colorBlack = "black";
+            cmdlineP->colorWhite = "white";
+        } else {
+            char * buffer = strdup(argv[1]);
+            char * hyphenPos = strchr(buffer, '-');
+            if (hyphenPos) {
+                *hyphenPos = '\0';
+                cmdlineP->colorBlack = buffer;
+                cmdlineP->colorWhite = hyphenPos+1;
+            } else {
+                cmdlineP->colorBlack = "black";
+                cmdlineP->colorWhite = buffer;
+            }
+        }
+        if (argc-1 < 2)
+            cmdlineP->inputFilename = "-";
+        else
+            cmdlineP->inputFilename = argv[2];
+        
+        if (argc-1 > 2)
+            pm_error("Program takes at most 2 arguments:  "
+                     "color name/range and input file name.  "
+                     "You specified %u", argc-1);
+    }
+}
+
+
+
+static void
+convertWithMap(FILE * const ifP,
+               unsigned int const cols,
+               unsigned int const rows,
+               gray         const maxval,
+               int          const format,
+               const char * const mapFileName,
+               FILE *       const ofP,
+               gray *       const grayrow,
+               pixel *      const pixelrow) {
+
+    unsigned int row;
+    FILE * mapFileP;
+    int mapcols, maprows;
     pixval mapmaxval;
-    char* color0;
-    char* color1;
+    pixel ** mappixels;
+    unsigned int mapmaxcolor;
+    
+    mapFileP = pm_openr(mapFileName);
+    mappixels = ppm_readppm(mapFileP, &mapcols, &maprows, &mapmaxval);
+    pm_close(mapFileP);
+    mapmaxcolor = maprows * mapcols - 1;
+
+    ppm_writeppminit(ofP, cols, rows, mapmaxval, 0);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+            
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int c;
+            if (maxval == mapmaxcolor)
+                c = grayrow[col];
+            else
+                c = grayrow[col] * mapmaxcolor / maxval;
+            pixelrow[col] = mappixels[c / mapcols][c % mapcols];
+        }
+        ppm_writeppmrow(ofP, pixelrow, cols, mapmaxval, 0);
+    }
+    ppm_freearray(mappixels, maprows);
+}
+
+
+
+static void
+convertLinear(FILE * const ifP,
+              unsigned int const cols,
+              unsigned int const rows,
+              gray         const maxval,
+              int          const format,
+              const char * const colorNameBlack,
+              const char * const colorNameWhite,
+              FILE *       const ofP,
+              gray *       const grayrow,
+              pixel *      const pixelrow) {
+
+    pixel colorBlack, colorWhite;
     pixval red0, grn0, blu0, red1, grn1, blu1;
-    const char* const usage = "<colorspec> [pgmfile]\n                 <colorspec1>,<colorspec2> [pgmfile]\n                 -map mapfile [pgmfile]";
-
-
-    ppm_init( &argc, argv );
-
-    argn = 1;
-    mappixels = (pixel**) 0;
-
-    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-map", 2 ) )
-	    {
-	    ++argn;
-	    if ( argn == argc )
-		pm_usage( usage );
-	    ifp = pm_openr( argv[argn] );
-	    mappixels = ppm_readppm( ifp, &mapcols, &maprows, &mapmaxval );
-	    pm_close( ifp );
-	    mapmaxcolor = maprows * mapcols - 1;
-	    }
-	else
-	    pm_usage( usage );
-	++argn;
-	}
-
-    if ( mappixels == (pixel**) 0 )
-	{
-	if ( argn == argc )
-	    pm_usage( usage );
-	color0 = argv[argn];
-	++argn;
-	}
-
-    if ( argn != argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
-    else
-	ifp = stdin;
+    unsigned int row;
+
+    ppm_writeppminit(ofP, cols, rows, maxval, 0);
+
+    colorBlack = ppm_parsecolor(colorNameBlack, maxval);
+    colorWhite = ppm_parsecolor(colorNameWhite, maxval);
+ 
+    red0 = PPM_GETR(colorBlack);
+    grn0 = PPM_GETG(colorBlack);
+    blu0 = PPM_GETB(colorBlack);
+    red1 = PPM_GETR(colorWhite);
+    grn1 = PPM_GETG(colorWhite);
+    blu1 = PPM_GETB(colorWhite);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            gray const input = grayrow[col];
+            PPM_ASSIGN(
+                pixelrow[col],
+                (red0 * (maxval - input) + red1 * input) / maxval,
+                (grn0 * (maxval - input) + grn1 * input) / maxval,
+                (blu0 * (maxval - input) + blu1 * input) / maxval);
+        }
+        ppm_writeppmrow(ofP, pixelrow, cols, maxval, 0);
+    }
+}
 
-    if ( argn != argc )
-	pm_usage( usage );
 
-    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
-    grayrow = pgm_allocrow( cols );
-    if ( mappixels == (pixel**) 0 )
-	ppm_writeppminit( stdout, cols, rows, (pixval) maxval, 0 );
-    else
-	ppm_writeppminit( stdout, cols, rows, mapmaxval, 0 );
-    pixelrow = ppm_allocrow( cols );
-
-    if ( mappixels == (pixel**) 0 )
-	{
-	color1 = strchr( color0, '-' );
-	if ( color1 == 0 )
-	    {
-	    color1 = color0;
-	    red0 = 0;
-	    grn0 = 0;
-	    blu0 = 0;
-	    }
-	else
-	    {
-	    *color1 = '\0';
-	    ++color1;
-	    p = ppm_parsecolor( color0, (pixval) maxval );
-	    red0 = PPM_GETR( p );
-	    grn0 = PPM_GETG( p );
-	    blu0 = PPM_GETB( p );
-	    }
-	p = ppm_parsecolor( color1, (pixval) maxval );
-	red1 = PPM_GETR( p );
-	grn1 = PPM_GETG( p );
-	blu1 = PPM_GETB( p );
-	}
-
-    for ( row = 0; row < rows; ++row )
-	{
-	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
-
-	if ( mappixels == (pixel**) 0 )
-	    {
-	    for ( col = 0, gP = grayrow, pP = pixelrow;
-		  col < cols;
-		  ++col, ++gP, ++pP )
-		PPM_ASSIGN(
-		    *pP,
-		    ( red0 * ( maxval - *gP ) + red1 * *gP ) / maxval,
-		    ( grn0 * ( maxval - *gP ) + grn1 * *gP ) / maxval,
-		    ( blu0 * ( maxval - *gP ) + blu1 * *gP ) / maxval );
-
-	    }
-	else
-	    {
-	    register int c;
-
-	    for ( col = 0, gP = grayrow, pP = pixelrow;
-		  col < cols;
-		  ++col, ++gP, ++pP )
-		{
-		if ( maxval == mapmaxcolor )
-		    c = *gP;
-		else
-		    c = *gP * mapmaxcolor / maxval;
-		*pP = mappixels[c / mapcols][c % mapcols];
-		}
-	    }
-
-    if (!mappixels)
-        ppm_writeppmrow( stdout, pixelrow, cols, (pixval) maxval, 0 );
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    struct cmdlineInfo cmdline;
+    gray * grayrow;
+    pixel * pixelrow;
+    int rows, cols, format;
+    gray maxval;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+    grayrow = pgm_allocrow(cols);
+    pixelrow = ppm_allocrow(cols);
+
+    if (cmdline.map)
+        convertWithMap(ifP, cols, rows, maxval, format, cmdline.map,
+                       stdout, grayrow, pixelrow);
     else
-        ppm_writeppmrow( stdout, pixelrow, cols, mapmaxval, 0 );
-	}
+        convertLinear(ifP, cols, rows, maxval, format, 
+                      cmdline.colorBlack, cmdline.colorWhite, stdout,
+                      grayrow, pixelrow);
 
-    pm_close( ifp );
+    ppm_freerow(pixelrow);
+    pgm_freerow(grayrow);
+    pm_close(ifP);
 
     /* If the program failed, it previously aborted with nonzero completion
        code, via various function calls.
     */
     return 0;
-    }
+}
diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c
new file mode 100644
index 00000000..89ac100a
--- /dev/null
+++ b/converter/other/pngtopam.c
@@ -0,0 +1,1281 @@
+/*
+** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de>
+**                        and Willem van Schaik <willem@schaik.com>
+**
+** 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.
+**
+** modeled after giftopnm by David Koblas and
+** with lots of bits pasted from libpng.txt by Guy Eric Schalnat
+*/
+
+#ifndef PNMTOPNG_WARNING_LEVEL
+#  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
+#endif                               /*  2 for warnings (1 == error) */
+
+
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <png.h>    /* includes zlib.h and setjmp.h */
+#define VERSION "2.37.4 (5 December 1999) +netpbm"
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+/* A hack until we can remove direct access to png_info from the program */
+#if PNG_LIBPNG_VER >= 10400
+#define trans_values trans_color
+#define TRANS_ALPHA trans_alpha
+#else
+#define TRANS_ALPHA trans
+#endif
+
+typedef struct _jmpbuf_wrapper {
+  jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX, ALPHA_IN};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* '-' if stdin */
+    unsigned int verbose;
+    enum alpha_handling alpha;
+    const char * background;
+    float gamma;  /* -1.0 means unspecified */
+    const char * text;
+    unsigned int time;
+};
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A color in a format compatible with the PNG library.
+
+   Note that the PNG library declares types png_color and png_color_16
+   which are similar.
+-----------------------------------------------------------------------------*/
+    png_uint_16 r;
+    png_uint_16 g;
+    png_uint_16 b;
+} pngcolor;
+
+
+static png_uint_16 maxval;
+static bool verbose;
+static jmpbuf_wrapper pngtopnm_jmpbuf_struct;
+
+
+static void
+parseCommandLine(int                  argc, 
+                 const char **        argv,
+                 struct cmdlineInfo * cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int alphaSpec, alphapamSpec, mixSpec,
+        backgroundSpec, gammaSpec, textSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,       0);
+    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,                  
+            &alphaSpec,               0);
+    OPTENT3(0, "alphapam",    OPT_FLAG,   NULL,                  
+            &alphapamSpec,            0);
+    OPTENT3(0, "mix",         OPT_FLAG,   NULL,                  
+            &mixSpec,                 0);
+    OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
+            &backgroundSpec,          0);
+    OPTENT3(0, "gamma",       OPT_FLOAT,  &cmdlineP->gamma,
+            &gammaSpec,               0);
+    OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
+            &textSpec,                0);
+    OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
+            &cmdlineP->time,          0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (alphaSpec + mixSpec + alphapamSpec > 1)
+        pm_error("You cannot specify more than one of -alpha -alphapam -mix");
+    else if (alphaSpec)
+        cmdlineP->alpha = ALPHA_ONLY;
+    else if (mixSpec)
+        cmdlineP->alpha = ALPHA_MIX;
+    else if (alphapamSpec)
+        cmdlineP->alpha = ALPHA_IN;
+    else
+        cmdlineP->alpha = ALPHA_NONE;
+
+    if (backgroundSpec && !mixSpec)
+        pm_error("-background is useless without -mix");
+
+    if (!backgroundSpec)
+        cmdlineP->background = NULL;
+
+    if (!gammaSpec)
+        cmdlineP->gamma = -1.0;
+
+    if (!textSpec)
+        cmdlineP->text = NULL;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Program takes at most one argument: input file name.  "
+            "you specified %d", argc-1);
+}
+
+
+
+static png_uint_16
+get_png_val(const png_byte ** const pp,
+            int               const bit_depth) {
+
+    png_uint_16 c;
+    
+    if (bit_depth == 16)
+        c = (*((*pp)++)) << 8;
+    else
+        c = 0;
+
+    c |= (*((*pp)++));
+    
+    return c;
+}
+
+
+
+static bool
+isGrayscale(pngcolor const color) {
+
+    return color.r == color.g && color.r == color.b;
+}
+
+
+
+static sample
+alphaMix(png_uint_16 const foreground,
+         png_uint_16 const background,
+         png_uint_16 const alpha,
+         sample      const maxval) {
+
+    double const opacity      = (double)alpha / maxval;
+    double const transparency = 1.0 - opacity;
+
+    return ROUNDU(foreground * opacity + background * transparency);
+}
+
+
+
+static void
+setTuple(const struct pam *  const pamP,
+         tuple               const tuple,
+         pngcolor            const foreground,
+         pngcolor            const background,
+         enum alpha_handling const alphaHandling,
+         png_uint_16         const alpha) {
+
+    if (alphaHandling == ALPHA_ONLY)
+        tuple[0] = alpha;
+    else if (alphaHandling == ALPHA_NONE ||
+             (alphaHandling == ALPHA_MIX && alpha == maxval)) {
+        if (pamP->depth < 3)
+            tuple[0] = foreground.r;
+        else {
+            tuple[PAM_RED_PLANE] = foreground.r;
+            tuple[PAM_GRN_PLANE] = foreground.g;
+            tuple[PAM_BLU_PLANE] = foreground.b;
+        }
+    } else if (alphaHandling == ALPHA_IN) {
+        if (pamP->depth < 4) {
+            tuple[0] = foreground.r;
+            tuple[PAM_GRAY_TRN_PLANE] = alpha;
+        } else {
+            tuple[PAM_RED_PLANE] = foreground.r;
+            tuple[PAM_GRN_PLANE] = foreground.g;
+            tuple[PAM_BLU_PLANE] = foreground.b;
+            tuple[PAM_TRN_PLANE] = alpha;
+        }    
+    } else {
+        assert(alphaHandling == ALPHA_MIX);
+
+        if (pamP->depth < 3)
+            tuple[0] =
+                alphaMix(foreground.r, background.r, alpha, maxval);
+        else {
+            tuple[PAM_RED_PLANE] =
+                alphaMix(foreground.r, background.r, alpha, maxval);
+            tuple[PAM_GRN_PLANE] =
+                alphaMix(foreground.g, background.g, alpha, maxval);
+            tuple[PAM_BLU_PLANE] =
+                alphaMix(foreground.b, background.b, alpha, maxval);
+        }
+    }
+}
+
+
+
+static png_uint_16
+gamma_correct(png_uint_16 const v,
+              float       const g) {
+
+    if (g != -1.0)
+        return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
+                                    maxval);
+    else
+        return v;
+}
+
+
+
+static int iscolor (png_color c)
+{
+  return c.red != c.green || c.green != c.blue;
+}
+
+static void save_text (png_info *info_ptr, FILE *tfp)
+{
+  int i, j, k;
+
+  for (i = 0 ; i < info_ptr->num_text ; i++) {
+    j = 0;
+    while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ')
+      j++;    
+    if (info_ptr->text[i].key[j] != ' ') {
+      fprintf (tfp, "%s", info_ptr->text[i].key);
+      for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++)
+        putc (' ', tfp);
+    } else {
+      fprintf (tfp, "\"%s\"", info_ptr->text[i].key);
+      for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++)
+        putc (' ', tfp);
+    }
+    putc (' ', tfp); /* at least one space between key and text */
+    
+    for (j = 0 ; j < info_ptr->text[i].text_length ; j++) {
+      putc (info_ptr->text[i].text[j], tfp);
+      if (info_ptr->text[i].text[j] == '\n')
+        for (k = 0 ; k < 16 ; k++)
+          putc ((int)' ', tfp);
+    }
+    putc ((int)'\n', tfp);
+  }
+}
+
+static void show_time (png_info *info_ptr)
+{
+    static const char * const month[] = {
+        "", "January", "February", "March", "April", "May", "June",
+        "July", "August", "September", "October", "November", "December"
+    };
+
+  if (info_ptr->valid & PNG_INFO_tIME) {
+    if (info_ptr->mod_time.month < 1 ||
+      info_ptr->mod_time.month >= ARRAY_SIZE(month)) {
+      pm_message("tIME chunk in PNG input is invalid; "
+                 "modification time of image is unknown.  "
+                 "The month value, which should be in the range "
+                 "1-12, is %u", info_ptr->mod_time.month);
+    } else
+    pm_message ("modification time: %02d %s %d %02d:%02d:%02d",
+                info_ptr->mod_time.day, month[info_ptr->mod_time.month],
+                info_ptr->mod_time.year, info_ptr->mod_time.hour,
+                info_ptr->mod_time.minute, info_ptr->mod_time.second);
+  }
+}
+
+static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
+{
+  jmpbuf_wrapper  *jmpbuf_ptr;
+
+  /* this function, aside from the extra step of retrieving the "error
+   * pointer" (below) and the fact that it exists within the application
+   * rather than within libpng, is essentially identical to libpng's
+   * default error handler.  The second point is critical:  since both
+   * setjmp() and longjmp() are called from the same code, they are
+   * guaranteed to have compatible notions of how big a jmp_buf is,
+   * regardless of whether _BSD_SOURCE or anything else has (or has not)
+   * been defined. */
+
+  pm_message("fatal libpng error: %s", msg);
+
+  jmpbuf_ptr = png_get_error_ptr(png_ptr);
+  if (jmpbuf_ptr == NULL) {
+      /* we are completely hosed now */
+      pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
+  }
+
+  longjmp(jmpbuf_ptr->jmpbuf, 1);
+}
+
+
+
+static void
+dump_png_info(png_info *info_ptr) {
+
+    const char *type_string;
+    const char *filter_string;
+
+    switch (info_ptr->color_type) {
+      case PNG_COLOR_TYPE_GRAY:
+        type_string = "gray";
+        break;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+        type_string = "gray+alpha";
+        break;
+
+      case PNG_COLOR_TYPE_PALETTE:
+        type_string = "palette";
+        break;
+
+      case PNG_COLOR_TYPE_RGB:
+        type_string = "truecolor";
+        break;
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+        type_string = "truecolor+alpha";
+        break;
+    }
+
+    switch (info_ptr->filter_type) {
+    case PNG_FILTER_TYPE_BASE:
+        asprintfN(&filter_string, "base filter");
+        break;
+    default:
+        asprintfN(&filter_string, "unknown filter type %d", 
+                  info_ptr->filter_type);
+    }
+
+    pm_message("reading a %ldw x %ldh image, %d bit%s",
+               info_ptr->width, info_ptr->height,
+               info_ptr->bit_depth, info_ptr->bit_depth > 1 ? "s" : "");
+    pm_message("%s, %s, %s",
+               type_string,
+               info_ptr->interlace_type ? 
+               "Adam7 interlaced" : "not interlaced",
+               filter_string);
+    pm_message("background {index, gray, red, green, blue} = "
+               "{%d, %d, %d, %d, %d}",
+               info_ptr->background.index,
+               info_ptr->background.gray,
+               info_ptr->background.red,
+               info_ptr->background.green,
+               info_ptr->background.blue);
+
+    strfree(filter_string);
+
+    if (info_ptr->valid & PNG_INFO_tRNS)
+        pm_message("tRNS chunk (transparency): %u entries",
+                   info_ptr->num_trans);
+    else
+        pm_message("tRNS chunk (transparency): not present");
+
+    if (info_ptr->valid & PNG_INFO_gAMA)
+        pm_message("gAMA chunk (image gamma): gamma = %4.2f", info_ptr->gamma);
+    else
+        pm_message("gAMA chunk (image gamma): not present");
+
+    if (info_ptr->valid & PNG_INFO_sBIT)
+        pm_message("sBIT chunk: present");
+    else
+        pm_message("sBIT chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_cHRM)
+        pm_message("cHRM chunk: present");
+    else
+        pm_message("cHRM chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_PLTE)
+        pm_message("PLTE chunk: %d entries", info_ptr->num_palette);
+    else
+        pm_message("PLTE chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_bKGD)
+        pm_message("bKGD chunk: present");
+    else
+        pm_message("bKGD chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_hIST)
+        pm_message("hIST chunk: present");
+    else
+        pm_message("hIST chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_pHYs)
+        pm_message("pHYs chunk: present");
+    else
+        pm_message("pHYs chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_oFFs)
+        pm_message("oFFs chunk: present");
+    else
+        pm_message("oFFs chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_tIME)
+        pm_message("tIME chunk: present");
+    else
+        pm_message("tIME chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_pCAL)
+        pm_message("pCAL chunk: present");
+    else
+        pm_message("pCAL chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_sRGB)
+        pm_message("sRGB chunk: present");
+    else
+        pm_message("sRGB chunk: not present");
+}
+
+
+
+static unsigned int
+computePngLineSize(png_info * const pngInfoP) {
+
+    unsigned int const bytesPerSample = pngInfoP->bit_depth == 16 ? 2 : 1;
+
+    unsigned int samplesPerPixel;
+
+    switch (pngInfoP->color_type) {
+    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
+    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
+    default:                        samplesPerPixel = 1;
+    }
+
+    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngInfoP->width)
+        pm_error("Width %u of PNG is uncomputably large",
+                 (unsigned int)pngInfoP->width);
+       
+    return pngInfoP->width * bytesPerSample * samplesPerPixel;
+}
+
+
+
+static void
+allocPngRaster(png_info *   const pngInfoP,
+               png_byte *** const pngImageP) {
+
+    unsigned int const lineSize = computePngLineSize(pngInfoP);
+
+    png_byte ** pngImage;
+    unsigned int row;
+
+    MALLOCARRAY(pngImage, pngInfoP->height);
+
+    if (pngImage == NULL)
+        pm_error("couldn't allocate space for %u PNG raster rows",
+                 (unsigned int)pngInfoP->height);
+
+    for (row = 0; row < pngInfoP->height; ++row) {
+        MALLOCARRAY(pngImage[row], lineSize);
+        if (pngImage[row] == NULL)
+            pm_error("couldn't allocate space for %uth row of PNG raster",
+                     row);
+    }
+    *pngImageP = pngImage;
+}
+
+
+
+static void
+freePngRaster(png_byte ** const pngRaster,
+              png_info *  const pngInfoP) {
+
+    unsigned int row;
+
+    for (row = 0; row < pngInfoP->height; ++row)
+        free(pngRaster[row]);
+
+    free(pngRaster);
+}
+
+
+
+static bool
+isTransparentColor(pngcolor   const color,
+                   png_info * const pngInfoP,
+                   double     const totalgamma) {
+/*----------------------------------------------------------------------------
+   Return TRUE iff pixels of color 'color' are supposed to be transparent
+   everywhere they occur.  Assume it's an RGB image.
+
+   'color' has been gamma-corrected.
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    if (pngInfoP->valid & PNG_INFO_tRNS) {
+        const png_color_16 * const transColorP = &pngInfoP->trans_values;
+
+        /* It seems odd that libpng lets you get gamma-corrected pixel
+           values, but not gamma-corrected transparency or background
+           values.  But as that is the case, we have to gamma-correct
+           the transparency values.
+
+           Note that because we compare the gamma-corrected values and
+           there may be many-to-one mapping of uncorrected to corrected
+           values, more pixels may be transparent than what the user
+           intended.
+
+           We could fix this by not letting libpng gamma-correct the
+           pixels, and just do it ourselves.
+        */
+    
+        switch (pngInfoP->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
+            break;
+        default:
+            retval = 
+                color.r == gamma_correct(transColorP->red,   totalgamma) &&
+                color.g == gamma_correct(transColorP->green, totalgamma) &&
+                color.b == gamma_correct(transColorP->blue,  totalgamma);
+        }
+    } else 
+        retval = FALSE;
+
+    return retval;
+}
+
+
+
+#define SIG_CHECK_SIZE 4
+
+static void
+read_sig_buf(FILE * const ifP) {
+
+    unsigned char sig_buf[SIG_CHECK_SIZE];
+    size_t bytesRead;
+
+    bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP);
+    if (bytesRead != SIG_CHECK_SIZE)
+        pm_error ("input file is empty or too short");
+
+    if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE)
+        != 0)
+        pm_error ("input file is not a PNG file");
+}
+
+
+
+static void
+setupGammaCorrection(png_struct * const png_ptr,
+                     png_info *   const info_ptr,
+                     float        const displaygamma,
+                     float *      const totalgammaP) {
+
+    if (displaygamma == -1.0)
+        *totalgammaP = -1.0;
+    else {
+        float imageGamma;
+        if (info_ptr->valid & PNG_INFO_gAMA)
+            imageGamma = info_ptr->gamma;
+        else {
+            if (verbose)
+                pm_message("PNG doesn't specify image gamma.  Assuming 1.0");
+            imageGamma = 1.0;
+        }
+
+        if (fabs(displaygamma * imageGamma - 1.0) < .01) {
+            *totalgammaP = -1.0;
+            if (verbose)
+                pm_message("image gamma %4.2f matches "
+                           "display gamma %4.2f.  No conversion.",
+                           imageGamma, displaygamma);
+        } else {
+            png_set_gamma(png_ptr, displaygamma, imageGamma);
+            *totalgammaP = imageGamma * displaygamma;
+            /* in case of gamma-corrections, sBIT's as in the
+               PNG-file are not valid anymore 
+            */
+            info_ptr->valid &= ~PNG_INFO_sBIT;
+            if (verbose)
+                pm_message("image gamma is %4.2f, "
+                           "converted for display gamma of %4.2f",
+                           imageGamma, displaygamma);
+        }
+    }
+}
+
+
+
+static bool
+paletteHasPartialTransparency(png_info * const info_ptr) {
+
+    bool retval;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (info_ptr->valid & PNG_INFO_tRNS) {
+            bool foundGray;
+            unsigned int i;
+            
+            for (i = 0, foundGray = FALSE;
+                 i < info_ptr->num_trans && !foundGray;
+                 ++i) {
+                if (info_ptr->TRANS_ALPHA[i] != 0 &&
+                    info_ptr->TRANS_ALPHA[i] != maxval) {
+                    foundGray = TRUE;
+                }
+            }
+            retval = foundGray;
+        } else
+            retval = FALSE;
+    } else
+        retval = FALSE;
+
+    return retval;
+}
+
+
+
+static void
+getComponentSbitFg(png_info * const pngInfoP,
+                   png_byte * const fgSbitP,
+                   bool *     const notUniformP) {
+
+    if (pngInfoP->color_type == PNG_COLOR_TYPE_RGB ||
+        pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+        pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (pngInfoP->sig_bit.red == pngInfoP->sig_bit.blue &&
+            pngInfoP->sig_bit.red == pngInfoP->sig_bit.green) {
+            *notUniformP = false;
+            *fgSbitP     = pngInfoP->sig_bit.red;
+        } else
+            *notUniformP = true;
+    } else {
+        /* It has only a gray channel so it's obviously uniform */
+        *notUniformP = false;
+        *fgSbitP     = pngInfoP->sig_bit.gray;
+    }
+}
+
+
+
+static void
+getComponentSbit(png_info *          const pngInfoP,
+                 enum alpha_handling const alphaHandling,
+                 png_byte *          const componentSbitP,
+                 bool *              const notUniformP) {
+
+    switch (alphaHandling) {
+
+    case ALPHA_ONLY:
+        /* We care only about the alpha channel, so the uniform Sbit is
+           the alpha Sbit
+        */
+        *notUniformP = false;
+        *componentSbitP = pngInfoP->sig_bit.alpha;
+        break;
+    case ALPHA_NONE:
+    case ALPHA_MIX:
+        /* We aren't going to produce an alpha channel, so we care only
+           about the uniformity of the foreground channels.
+        */
+        getComponentSbitFg(pngInfoP, componentSbitP, notUniformP);
+        break;
+    case ALPHA_IN: {
+        /* We care about both the foreground and the alpha */
+        bool fgNotUniform;
+        png_byte fgSbit;
+        
+        getComponentSbitFg(pngInfoP, &fgSbit, &fgNotUniform);
+
+        if (fgNotUniform)
+            *notUniformP = true;
+        else {
+            if (fgSbit == pngInfoP->sig_bit.alpha) {
+                *notUniformP    = false;
+                *componentSbitP = fgSbit;
+            } else
+                *notUniformP = true;
+        }
+    } break;
+    }
+}
+
+                 
+
+static void
+shiftPalette(png_info *   const pngInfoP,
+             unsigned int const shift) {
+/*----------------------------------------------------------------------------
+   Shift every component of every color in the PNG palette right by
+   'shift' bits because sBIT chunk says only those are significant.
+-----------------------------------------------------------------------------*/
+    if (shift > 7)
+        pm_error("Invalid PNG: paletted image can't have "
+                 "more than 8 significant bits per component, "
+                 "but sBIT chunk says %u bits",
+                 shift);
+    else {
+        unsigned int i;
+        
+        for (i = 0; i < pngInfoP->num_palette; ++i) {
+            pngInfoP->palette[i].red   >>= (8 - shift);
+            pngInfoP->palette[i].green >>= (8 - shift);
+            pngInfoP->palette[i].blue  >>= (8 - shift);
+        }
+    }
+}
+
+
+
+static void
+computeMaxvalFromSbit(png_struct *        const pngP,
+                      png_info *          const pngInfoP,
+                      enum alpha_handling const alphaHandling,
+                      png_uint_16 *       const maxvalP,
+                      bool *              const succeededP,
+                      int *               const errorlevelP) {
+
+    /* sBIT handling is very tricky. If we are extracting only the
+       image, we can use the sBIT info for grayscale and color images,
+       if the three values agree. If we extract the transparency/alpha
+       mask, sBIT is irrelevant for trans and valid for alpha. If we
+       mix both, the multiplication may result in values that require
+       the normal bit depth, so we will use the sBIT info only for
+       transparency, if we know that only solid and fully transparent
+       is used 
+    */
+
+    bool notUniform;
+        /* The sBIT chunk says the number of significant high-order bits
+           in each component varies among the components we care about.
+        */
+    png_byte componentSigBit;
+        /* The number of high-order significant bits in each RGB component.
+           Meaningless if they aren't all the same (i.e. 'notUniform')
+        */
+
+    getComponentSbit(pngInfoP, alphaHandling, &componentSigBit, &notUniform);
+
+    if (notUniform) {
+        pm_message("This program cannot handle "
+                   "different bit depths for color channels");
+        pm_message("writing file with %u bit resolution", pngInfoP->bit_depth);
+        *succeededP = false;
+        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+    } else if (componentSigBit > 15) {
+        pm_message("Invalid PNG: says %u significant bits for a component; "
+                   "max possible is 16.  Ignoring sBIT chunk.",
+                   componentSigBit);
+        *succeededP = false;
+        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+    } else {
+        if (alphaHandling == ALPHA_MIX &&
+            (pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+             pngInfoP->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+             paletteHasPartialTransparency(pngInfoP)))
+            *succeededP = false;
+        else {
+            if (componentSigBit < pngInfoP->bit_depth) {
+                pm_message("Image has fewer significant bits, "
+                           "writing file with %u bits", componentSigBit);
+                *maxvalP = (1l << componentSigBit) - 1;
+                *succeededP = true;
+
+                if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE)
+                    shiftPalette(pngInfoP, componentSigBit);
+                else
+                    png_set_shift(pngP, &pngInfoP->sig_bit);
+            } else
+                *succeededP = false;
+        }
+    }
+}
+
+
+
+static void
+setupSignificantBits(png_struct *        const pngP,
+                     png_info *          const pngInfoP,
+                     enum alpha_handling const alphaHandling,
+                     png_uint_16 *       const maxvalP,
+                     int *               const errorlevelP) {
+/*----------------------------------------------------------------------------
+  Figure out what maxval would best express the information in the PNG
+  described by *pngP and *pngInfoP, with 'alpha' telling which
+  information in the PNG we care about (image or alpha mask).
+
+  Return the result as *maxvalP.
+
+  Also set up *pngP for the corresponding significant bits.
+-----------------------------------------------------------------------------*/
+    bool gotItFromSbit;
+    
+    if (pngInfoP->valid & PNG_INFO_sBIT)
+        computeMaxvalFromSbit(pngP, pngInfoP, alphaHandling,
+                              maxvalP, &gotItFromSbit, errorlevelP);
+    else
+        gotItFromSbit = false;
+
+    if (!gotItFromSbit) {
+        if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE) {
+            if (alphaHandling == ALPHA_ONLY) {
+                if (pngInfoP->color_type == PNG_COLOR_TYPE_GRAY ||
+                    pngInfoP->color_type == PNG_COLOR_TYPE_RGB)
+                    /* The alpha mask will be all opaque, so maxval 1
+                       is plenty
+                    */
+                    *maxvalP = 1;
+                else if (paletteHasPartialTransparency(pngInfoP))
+                    /* Use same maxval as PNG transparency palette for
+                       simplicity
+                    */
+                    *maxvalP = 255;
+                else
+                    /* A common case, so we conserve bits */
+                    *maxvalP = 1;
+            } else
+                /* Use same maxval as PNG palette for simplicity */
+                *maxvalP = 255;
+        } else {
+            *maxvalP = (1l << pngInfoP->bit_depth) - 1;
+        }
+    }
+}
+
+
+
+static bool
+imageHasColor(png_info * const info_ptr) {
+
+    bool retval;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+
+        retval = FALSE;
+    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        bool foundColor;
+        unsigned int i;
+            
+        for (i = 0, foundColor = FALSE;
+             i < info_ptr->num_palette && !foundColor;
+             ++i) {
+            if (iscolor(info_ptr->palette[i]))
+                foundColor = TRUE;
+        }
+        retval = foundColor;
+    } else
+        retval = TRUE;
+
+    return retval;
+}
+
+
+
+static void
+determineOutputType(png_info *          const pngInfoP,
+                    enum alpha_handling const alphaHandling,
+                    pngcolor            const bgColor,
+                    xelval              const maxval,
+                    int *               const formatP,
+                    unsigned int *      const depthP,
+                    char *              const tupleType) {
+
+    if (alphaHandling == ALPHA_ONLY) {
+        /* The output is a old style pseudo-PNM transparency image */
+        *depthP = 1;
+        *formatP = maxval > 1 ? PGM_FORMAT : PBM_FORMAT;
+    } else {            
+        /* The output is a normal Netpbm image */
+        bool const outputIsColor =
+            imageHasColor(pngInfoP) || !isGrayscale(bgColor);
+
+        if (alphaHandling == ALPHA_IN) {
+            *formatP = PAM_FORMAT;
+            if (outputIsColor) {
+                *depthP = 4;
+                strcpy(tupleType, "RGB_ALPHA");
+            } else {
+                *depthP = 2;
+                strcpy(tupleType, "GRAYSCALE_ALPHA");
+            }
+        } else {
+            if (outputIsColor) {
+                *formatP = PPM_FORMAT;
+                *depthP = 3;
+            } else {
+                *depthP = 1;
+                *formatP = maxval > 1 ? PGM_FORMAT : PBM_FORMAT;
+            }
+        }
+    }
+}
+
+
+
+static void
+getBackgroundColor(png_info *   const info_ptr,
+                   const char * const requestedColor,
+                   float        const totalgamma,
+                   xelval       const maxval,
+                   pngcolor *   const bgColorP) {
+/*----------------------------------------------------------------------------
+   Figure out what the background color should be.  If the user requested
+   a particular color ('requestedColor' not null), that's the one.
+   Otherwise, if the PNG specifies a background color, that's the one.
+   And otherwise, it's white.
+-----------------------------------------------------------------------------*/
+    if (requestedColor) {
+        /* Background was specified from the command-line; we always
+           use that.  I chose to do no gamma-correction in this case;
+           which is a bit arbitrary.  
+        */
+        pixel const backcolor = ppm_parsecolor(requestedColor, maxval);
+
+        bgColorP->r = PPM_GETR(backcolor);
+        bgColorP->g = PPM_GETG(backcolor);
+        bgColorP->b = PPM_GETB(backcolor);
+
+    } else if (info_ptr->valid & PNG_INFO_bKGD) {
+        /* didn't manage to get libpng to work (bugs?) concerning background
+           processing, therefore we do our own.
+        */
+        switch (info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            bgColorP->r = bgColorP->g = bgColorP->b = 
+                gamma_correct(info_ptr->background.gray, totalgamma);
+            break;
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_color const rawBgcolor = 
+                info_ptr->palette[info_ptr->background.index];
+            bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
+            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
+            bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
+        }
+        break;
+        case PNG_COLOR_TYPE_RGB:
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            png_color_16 const rawBgcolor = info_ptr->background;
+            
+            bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
+            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
+            bgColorP->b = gamma_correct(rawBgcolor.blue,  totalgamma);
+        }
+        break;
+        }
+    } else 
+        /* when no background given, we use white [from version 2.37] */
+        bgColorP->r = bgColorP->g = bgColorP->b = maxval;
+}
+
+
+
+#define GET_PNG_VAL(p) get_png_val(&(p), pngInfoP->bit_depth)
+
+
+
+static void
+makeTupleRow(const struct pam *  const pamP,
+             const tuple *       const tuplerow,
+             png_info *          const pngInfoP,
+             const png_byte *    const pngRasterRow,
+             pngcolor            const bgColor,
+             enum alpha_handling const alphaHandling,
+             double              const totalgamma) {
+
+    const png_byte * pngPixelP;
+    unsigned int col;
+
+    pngPixelP = &pngRasterRow[0];  /* initial value */
+    for (col = 0; col < pngInfoP->width; ++col) {
+        switch (pngInfoP->color_type) {
+        case PNG_COLOR_TYPE_GRAY: {
+            pngcolor fgColor;
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
+                   isTransparentColor(fgColor, pngInfoP, totalgamma) ?
+                   0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_GRAY_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor,
+                     alphaHandling, alpha);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
+            png_color   const paletteColor = pngInfoP->palette[index];
+
+            pngcolor fgColor;
+
+            fgColor.r = paletteColor.red;
+            fgColor.g = paletteColor.green;
+            fgColor.b = paletteColor.blue;
+
+            setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
+                     (pngInfoP->valid & PNG_INFO_tRNS) &&
+                     index < pngInfoP->num_trans ?
+                     pngInfoP->TRANS_ALPHA[index] : maxval);
+        }
+        break;
+                
+        case PNG_COLOR_TYPE_RGB: {
+            pngcolor fgColor;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
+                     isTransparentColor(fgColor, pngInfoP, totalgamma) ?
+                     0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha     = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor,
+                     alphaHandling, alpha);
+        }
+        break;
+
+        default:
+            pm_error("unknown PNG color type: %d", pngInfoP->color_type);
+        }
+    }
+}
+
+
+
+static void
+reportOutputFormat(const struct pam * const pamP) {
+
+    switch (pamP->format) {
+
+    case PBM_FORMAT:
+        pm_message("Writing a PBM file");
+        break;
+    case PGM_FORMAT:
+        pm_message("Writing a PGM file with maxval %lu", pamP->maxval);
+        break;
+    case PPM_FORMAT:
+        pm_message("Writing a PPM file with maxval %lu", pamP->maxval);
+        break;
+    case PAM_FORMAT:
+        pm_message("Writing a PAM file with tuple type %s, maxval %u",
+                   pamP->tuple_type, maxval);
+        break;
+    default:
+        assert(false); /* Every possible value handled above */
+    }
+}
+    
+
+
+static void
+writeNetpbm(struct pam *        const pamP,
+            png_info *          const pngInfoP,
+            png_byte **         const pngRaster,
+            pngcolor            const bgColor,
+            enum alpha_handling const alphaHandling,
+            double              const totalgamma) {
+/*----------------------------------------------------------------------------
+   Write a Netpbm image of either the image or the alpha mask, according to
+   'alphaHandling' that is in the PNG image described by 'pngInfoP' and
+   pngRaster.
+
+   *pamP describes the required output image and is consistent with
+   *pngInfoP.
+
+   Use background color 'bgColor' in the output if the PNG is such that a
+   background color is needed.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+
+    if (verbose)
+        reportOutputFormat(pamP);
+
+    pnm_writepaminit(pamP);
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < pngInfoP->height; ++row) {
+        makeTupleRow(pamP, tuplerow, pngInfoP, pngRaster[row], bgColor,
+                     alphaHandling, totalgamma);
+
+        pnm_writepamrow(pamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void 
+convertpng(FILE *             const ifp, 
+           FILE *             const tfp, 
+           struct cmdlineInfo const cmdline,
+           int *              const errorlevelP) {
+
+    png_struct * png_ptr;
+    png_info * info_ptr;
+    png_byte ** png_image;
+    pngcolor bgColor;
+    float totalgamma;
+    struct pam pam;
+
+    *errorlevelP = 0;
+
+    read_sig_buf(ifp);
+
+    png_ptr = png_create_read_struct(
+        PNG_LIBPNG_VER_STRING,
+        &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
+    if (png_ptr == NULL)
+        pm_error("cannot allocate main libpng structure (png_ptr)");
+
+    info_ptr = png_create_info_struct (png_ptr);
+    if (info_ptr == NULL)
+        pm_error("cannot allocate LIBPNG structures");
+
+    if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf))
+        pm_error ("setjmp returns error condition");
+
+    png_init_io (png_ptr, ifp);
+    png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE);
+    png_read_info (png_ptr, info_ptr);
+
+    allocPngRaster(info_ptr, &png_image);
+
+    if (info_ptr->bit_depth < 8)
+        png_set_packing (png_ptr);
+
+    setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma);
+
+    setupSignificantBits(png_ptr, info_ptr, cmdline.alpha,
+                         &maxval, errorlevelP);
+
+    getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval,
+                       &bgColor);
+
+    png_read_image(png_ptr, png_image);
+    png_read_end(png_ptr, info_ptr);
+
+    if (verbose)
+        /* 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.
+        */
+        dump_png_info(info_ptr);
+
+    if (cmdline.time)
+        show_time(info_ptr);
+    if (tfp)
+        save_text(info_ptr, tfp);
+
+    if (info_ptr->valid & PNG_INFO_pHYs) {
+        float const r =
+            (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit;
+        if (r != 1.0) {
+            pm_message ("warning - non-square pixels; "
+                        "to fix do a 'pamscale -%cscale %g'",
+                        r < 1.0 ? 'x' : 'y',
+                        r < 1.0 ? 1.0 / r : r );
+            *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+
+    pam.size        = sizeof(pam);
+    pam.len         = PAM_STRUCT_SIZE(tuple_type);
+    pam.file        = stdout;
+    pam.plainformat = 0;
+    pam.height      = info_ptr->height;
+    pam.width       = info_ptr->width;
+    pam.maxval      = maxval;
+
+    determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval,
+                        &pam.format, &pam.depth, pam.tuple_type);
+
+    writeNetpbm(&pam, info_ptr, png_image, bgColor, cmdline.alpha, totalgamma);
+
+    fflush(stdout);
+
+    freePngRaster(png_image, info_ptr);
+
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+}
+
+
+
+int 
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * tfP;
+    int errorlevel;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    if (cmdline.text)
+        tfP = pm_openw(cmdline.text);
+    else
+        tfP = NULL;
+
+    convertpng(ifP, tfP, cmdline, &errorlevel);
+
+    if (tfP)
+        pm_close(tfP);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return errorlevel;
+}
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c
index 59149acd..a8ea25a7 100644
--- a/converter/other/pngtopnm.c
+++ b/converter/other/pngtopnm.c
@@ -1,6 +1,6 @@
 /*
 ** pngtopnm.c -
-** read a Portable Network Graphics file and produce a portable anymap
+** read a Portable Network Graphics file and produce a PNM.
 **
 ** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de>
 **                        and Willem van Schaik <willem@schaik.com>
@@ -16,61 +16,29 @@
 ** with lots of bits pasted from libpng.txt by Guy Eric Schalnat
 */
 
-/* 
-   BJH 20000408:  rename PPM_MAXMAXVAL to PPM_OVERALLMAXVAL
-   BJH 20000303:  fix include statement so dependencies work out right.
-*/
-/* GRR 19991203:  moved VERSION to new version.h header file */
-
-/* GRR 19990713:  fixed redundant freeing of png_ptr and info_ptr in setjmp()
- *  blocks and added "pm_close(ifp)" in each.  */
-
-/* GRR 19990317:  declared "clobberable" automatic variables in convertpng()
- *  static to fix Solaris/gcc stack-corruption bug.  Also installed custom
- *  error-handler to avoid jmp_buf size-related problems (i.e., jmp_buf
- *  compiled with one size in libpng and another size here).  */
-
 #ifndef PNMTOPNG_WARNING_LEVEL
 #  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
 #endif                               /*  2 for warnings (1 == error) */
 
+#include <assert.h>
 #include <math.h>
 #include <float.h>
 #include <png.h>    /* includes zlib.h and setjmp.h */
 #define VERSION "2.37.4 (5 December 1999) +netpbm"
 
-#include "pnm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "pnm.h"
 
+/* A hack until we can remove direct access to png_info from the program */
 #if PNG_LIBPNG_VER >= 10400
-#error Your PNG library (<png.h>) is incompatible with this Netpbm source code.
-#error You need either an older PNG library (older than 1.4)
-#error newer Netpbm source code (at least 10.48)
-#endif
-
-typedef struct _jmpbuf_wrapper {
-  jmp_buf jmpbuf;
-} jmpbuf_wrapper;
-
-/* GRR 19991205:  this is used as a test for pre-1999 versions of netpbm and
- *   pbmplus vs. 1999 or later (in which pm_close was split into two)
- */
-#ifdef PBMPLUS_RAWBITS
-#  define pm_closer pm_close
-#  define pm_closew pm_close
+#define TRANS_ALPHA trans_alpha
+#else
+#define TRANS_ALPHA trans
 #endif
 
-#ifndef TRUE
-#  define TRUE 1
-#endif
-#ifndef FALSE
-#  define FALSE 0
-#endif
-#ifndef NONE
-#  define NONE 0
-#endif
 
 enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX};
 
@@ -88,7 +56,7 @@ struct cmdlineInfo {
 };
 
 
-typedef struct pngcolor {
+typedef struct {
 /*----------------------------------------------------------------------------
    A color in a format compatible with the PNG library.
 
@@ -102,15 +70,13 @@ typedef struct pngcolor {
 
 
 static png_uint_16 maxval;
-static int verbose = FALSE;
-static int mtime;
-static jmpbuf_wrapper pngtopnm_jmpbuf_struct;
+static bool verbose;
 
 
 static void
-parseCommandLine(int                 argc, 
-                 char **             argv,
-                 struct cmdlineInfo *cmdlineP ) {
+parseCommandLine(int                  argc, 
+                 const char **        argv,
+                 struct cmdlineInfo * cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -121,7 +87,7 @@ parseCommandLine(int                 argc,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -130,6 +96,8 @@ parseCommandLine(int                 argc,
 
     unsigned int alphaSpec, mixSpec, backgroundSpec, gammaSpec, textSpec;
 
+    MALLOCARRAY(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
             &cmdlineP->verbose,       0);
@@ -150,7 +118,7 @@ parseCommandLine(int                 argc,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -186,12 +154,215 @@ parseCommandLine(int                 argc,
 
 
 
+static void
+pngtopnmErrorHandler(png_structp     const png_ptr,
+                     png_const_charp const msg) {
+
+    jmp_buf * jmpbufP;
+
+    /* this function, aside from the extra step of retrieving the "error
+       pointer" (below) and the fact that it exists within the application
+       rather than within libpng, is essentially identical to libpng's
+       default error handler.  The second point is critical:  since both
+       setjmp() and longjmp() are called from the same code, they are
+       guaranteed to have compatible notions of how big a jmp_buf is,
+       regardless of whether _BSD_SOURCE or anything else has (or has not)
+       been defined.
+    */
+
+    pm_message("fatal libpng error: %s", msg);
+
+    jmpbufP = png_get_error_ptr(png_ptr);
+
+    if (!jmpbufP) {
+        /* we are completely hosed now */
+        pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
+    }
+
+    longjmp(*jmpbufP, 1);
+}
+
+
+
+struct pngx {
+    png_structp png_ptr;
+    png_infop info_ptr;
+};
+
+
+
+static void
+pngx_createRead(struct pngx ** const pngxPP,
+                jmp_buf *      const jmpbufP) {
+
+    struct pngx * pngxP;
+
+    MALLOCVAR(pngxP);
+
+    if (!pngxP)
+        pm_error("Failed to allocate memory for PNG object");
+    else {
+        pngxP->png_ptr = png_create_read_struct(
+            PNG_LIBPNG_VER_STRING,
+            jmpbufP, pngtopnmErrorHandler, NULL);
+
+        if (!pngxP->png_ptr)
+            pm_error("cannot allocate main libpng structure (png_ptr)");
+        else {
+            pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr);
+
+            if (!pngxP->info_ptr)
+                pm_error("cannot allocate libpng info structure (info_ptr)");
+            else
+                *pngxPP = pngxP;
+        }
+    }
+}
+
+
+
+static void
+pngx_destroy(struct pngx * const pngxP) {
+
+    png_destroy_read_struct(&pngxP->png_ptr, &pngxP->info_ptr, NULL);
+
+    free(pngxP);
+}
+
+
+
+static bool
+pngx_chunkIsPresent(struct pngx * const pngxP,
+                    uint32_t      const chunkType) {
+
+    return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType);
+}
+
+
+
+static void
+verifyFileIsPng(FILE *   const ifP,
+                size_t * const consumedByteCtP) {
+
+    unsigned char buffer[4];
+    size_t bytesRead;
+
+    bytesRead = fread(buffer, 1, sizeof(buffer), ifP);
+    if (bytesRead != sizeof(buffer))
+        pm_error("input file is empty or too short");
+
+    if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0)
+        pm_error("input file is not a PNG file "
+                 "(does not have the PNG signature in its first 4 bytes)");
+    else
+        *consumedByteCtP = bytesRead;
+}
+
+
+
+static unsigned int
+computePngLineSize(struct pngx * const pngxP) {
+
+    unsigned int const bytesPerSample =
+        pngxP->info_ptr->bit_depth == 16 ? 2 : 1;
+
+    unsigned int samplesPerPixel;
+
+    switch (pngxP->info_ptr->color_type) {
+    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
+    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
+    default:                        samplesPerPixel = 1;
+    }
+
+    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width)
+        pm_error("Width %u of PNG is uncomputably large",
+                 (unsigned int)pngxP->info_ptr->width);
+       
+    return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel;
+}
+
+
+
+static void
+allocPngRaster(struct pngx * const pngxP,
+               png_byte ***  const pngImageP) {
+
+    unsigned int const lineSize = computePngLineSize(pngxP);
+
+    png_byte ** pngImage;
+    unsigned int row;
+
+    MALLOCARRAY(pngImage, pngxP->info_ptr->height);
+
+    if (pngImage == NULL)
+        pm_error("couldn't allocate space for %u PNG raster rows",
+                 (unsigned int)pngxP->info_ptr->height);
+
+    for (row = 0; row < pngxP->info_ptr->height; ++row) {
+        MALLOCARRAY(pngImage[row], lineSize);
+        if (pngImage[row] == NULL)
+            pm_error("couldn't allocate space for %uth row of PNG raster",
+                     row);
+    }
+    *pngImageP = pngImage;
+}
+
+
+
+static void
+freePngRaster(png_byte **   const pngRaster,
+              struct pngx * const pngxP) {
+
+    unsigned int row;
+
+    for (row = 0; row < pngxP->info_ptr->height; ++row)
+        free(pngRaster[row]);
+
+    free(pngRaster);
+}
+
+
+
+static void
+readPng(struct pngx * const pngxP,
+        FILE *        const ifP,
+        png_byte ***  const pngRasterP) {
+
+    size_t sigByteCt;
+    png_byte ** pngRaster;
+            
+    verifyFileIsPng(ifP, &sigByteCt);
+
+    /* Declare that we already read the signature bytes */
+    png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt);
+
+    png_init_io(pngxP->png_ptr, ifP);
+
+    png_read_info(pngxP->png_ptr, pngxP->info_ptr);
+
+    allocPngRaster(pngxP, &pngRaster);
+
+    if (pngxP->info_ptr->bit_depth < 8)
+        png_set_packing(pngxP->png_ptr);
+
+    png_read_image(pngxP->png_ptr, pngRaster);
+
+    png_read_end(pngxP->png_ptr, pngxP->info_ptr);
+
+    /* 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.
+    */
+
+    *pngRasterP = pngRaster;
+}
+
 
-#define get_png_val(p) _get_png_val (&(p), info_ptr->bit_depth)
 
 static png_uint_16
-_get_png_val (png_byte ** const pp,
-              int         const bit_depth) {
+get_png_val(const png_byte ** const pp,
+            int               const bit_depth) {
 
     png_uint_16 c;
     
@@ -247,121 +418,95 @@ gamma_correct(png_uint_16 const v,
               float       const g) {
 
     if (g != -1.0)
-        return (png_uint_16) (pow ((double) v / maxval, 
-                                   (1.0 / g)) * maxval + 0.5);
+        return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
+                                    maxval);
     else
         return v;
 }
 
 
 
-#ifdef __STDC__
-static int iscolor (png_color c)
-#else
-static int iscolor (c)
-png_color c;
-#endif
-{
-  return c.red != c.green || c.green != c.blue;
+static bool
+iscolor(png_color const c) {
+
+    return c.red != c.green || c.green != c.blue;
 }
 
-#ifdef __STDC__
-static void save_text (png_info *info_ptr, FILE *tfp)
-#else
-static void save_text (info_ptr, tfp)
-png_info *info_ptr;
-FILE *tfp;
-#endif
-{
-  int i, j, k;
-
-  for (i = 0 ; i < info_ptr->num_text ; i++) {
-    j = 0;
-    while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ')
-      j++;    
-    if (info_ptr->text[i].key[j] != ' ') {
-      fprintf (tfp, "%s", info_ptr->text[i].key);
-      for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++)
-        putc (' ', tfp);
-    } else {
-      fprintf (tfp, "\"%s\"", info_ptr->text[i].key);
-      for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++)
-        putc (' ', tfp);
-    }
-    putc (' ', tfp); /* at least one space between key and text */
+
+
+static void
+saveText(struct pngx * const pngxP,
+         FILE *        const tfP) {
+
+    png_info * const info_ptr = pngxP->info_ptr;
+
+    unsigned int i;
+
+    for (i = 0 ; i < info_ptr->num_text; ++i) {
+        unsigned int j;
+        j = 0;
+
+        while (info_ptr->text[i].key[j] != '\0' &&
+               info_ptr->text[i].key[j] != ' ')
+            ++j;    
+
+        if (info_ptr->text[i].key[j] != ' ') {
+            fprintf(tfP, "%s", info_ptr->text[i].key);
+            for (j = strlen (info_ptr->text[i].key); j < 15; ++j)
+                putc(' ', tfP);
+        } else {
+            fprintf(tfP, "\"%s\"", info_ptr->text[i].key);
+            for (j = strlen (info_ptr->text[i].key); j < 13; ++j)
+                putc(' ', tfP);
+        }
+        putc(' ', tfP); /* at least one space between key and text */
     
-    for (j = 0 ; j < info_ptr->text[i].text_length ; j++) {
-      putc (info_ptr->text[i].text[j], tfp);
-      if (info_ptr->text[i].text[j] == '\n')
-        for (k = 0 ; k < 16 ; k++)
-          putc ((int)' ', tfp);
+        for (j = 0; j < info_ptr->text[i].text_length; ++j) {
+            putc(info_ptr->text[i].text[j], tfP);
+            if (info_ptr->text[i].text[j] == '\n') {
+                unsigned int k;
+                for (k = 0; k < 16; ++k)
+                    putc(' ', tfP);
+            }
+        }
+        putc('\n', tfP);
     }
-    putc ((int)'\n', tfp);
-  }
 }
 
-#ifdef __STDC__
-static void show_time (png_info *info_ptr)
-#else
-static void show_time (info_ptr)
-png_info *info_ptr;
-#endif
-{
+
+
+static void
+showTime(struct pngx * const pngxP) {
+
     static const char * const month[] = {
         "", "January", "February", "March", "April", "May", "June",
         "July", "August", "September", "October", "November", "December"
     };
 
-  if (info_ptr->valid & PNG_INFO_tIME) {
-    if (info_ptr->mod_time.month < 1 ||
-      info_ptr->mod_time.month >= ARRAY_SIZE(month)) {
-      pm_message("tIME chunk in PNG input is invalid; "
-                 "modification time of image is unknown.  "
-                 "The month value, which should be in the range "
-                 "1-12, is %u", info_ptr->mod_time.month);
-    } else
-    pm_message ("modification time: %02d %s %d %02d:%02d:%02d",
-                info_ptr->mod_time.day, month[info_ptr->mod_time.month],
-                info_ptr->mod_time.year, info_ptr->mod_time.hour,
-                info_ptr->mod_time.minute, info_ptr->mod_time.second);
-  }
-}
-
-#ifdef __STDC__
-static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
-#else
-static void pngtopnm_error_handler (png_ptr, msg)
-png_structp png_ptr;
-png_const_charp msg;
-#endif
-{
-  jmpbuf_wrapper  *jmpbuf_ptr;
-
-  /* this function, aside from the extra step of retrieving the "error
-   * pointer" (below) and the fact that it exists within the application
-   * rather than within libpng, is essentially identical to libpng's
-   * default error handler.  The second point is critical:  since both
-   * setjmp() and longjmp() are called from the same code, they are
-   * guaranteed to have compatible notions of how big a jmp_buf is,
-   * regardless of whether _BSD_SOURCE or anything else has (or has not)
-   * been defined. */
-
-  pm_message("fatal libpng error: %s", msg);
-
-  jmpbuf_ptr = png_get_error_ptr(png_ptr);
-  if (jmpbuf_ptr == NULL) {
-      /* we are completely hosed now */
-      pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
-  }
-
-  longjmp(jmpbuf_ptr->jmpbuf, 1);
+    if (pngxP->info_ptr->valid & PNG_INFO_tIME) {
+      if (pngxP->info_ptr->mod_time.month < 1 ||
+        pngxP->info_ptr->mod_time.month >= ARRAY_SIZE(month)) {
+        pm_message("tIME chunk in PNG input is invalid; "
+                   "modification time of image is unknown.  "
+                   "The month value, which should be in the range "
+                   "1-12, is %u", pngxP->info_ptr->mod_time.month);
+      } else
+        pm_message("modification time: %02d %s %d %02d:%02d:%02d",
+                   pngxP->info_ptr->mod_time.day,
+                   month[pngxP->info_ptr->mod_time.month],
+                   pngxP->info_ptr->mod_time.year,
+                   pngxP->info_ptr->mod_time.hour,
+                   pngxP->info_ptr->mod_time.minute,
+                   pngxP->info_ptr->mod_time.second);
+    }
 }
 
 
 
 static void
-dump_png_info(png_info *info_ptr) {
+dumpPngInfo(struct pngx * const pngxP) {
 
+    png_info * const info_ptr = pngxP->info_ptr;
     const char *type_string;
     const char *filter_string;
 
@@ -445,7 +590,7 @@ dump_png_info(png_info *info_ptr) {
     else
         pm_message("bKGD chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_hIST)
+    if (info_ptr->valid & PNG_INFO_PLTE)
         pm_message("hIST chunk: present");
     else
         pm_message("hIST chunk: not present");
@@ -478,29 +623,62 @@ dump_png_info(png_info *info_ptr) {
 
 
 
+static const png_color_16 *
+transColor(struct pngx * const pngxP) {
+
+    png_bytep trans;
+    int numTrans;
+    png_color_16 * transColor;
+
+    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
+    
+    png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                 &trans, &numTrans, &transColor);
+
+    return transColor;
+}
+
+
+
 static bool
-isTransparentColor(pngcolor   const color,
-                   png_info * const info_ptr,
-                   double     const totalgamma) {
+isTransparentColor(pngcolor      const color,
+                   struct pngx * const pngxP,
+                   double        const totalgamma) {
 /*----------------------------------------------------------------------------
    Return TRUE iff pixels of color 'color' are supposed to be transparent
    everywhere they occur.  Assume it's an RGB image.
+
+   'color' has been gamma-corrected.
 -----------------------------------------------------------------------------*/
     bool retval;
 
-    if (info_ptr->valid & PNG_INFO_tRNS) {
-        const png_color_16 * const transColorP = &info_ptr->trans_values;
-    
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        const png_color_16 * const transColorP = transColor(pngxP);
 
-        /* There seems to be a problem here: you can't compare real
-           numbers for equality.  Also, I'm not sure the gamma
-           corrected/uncorrected color spaces are right here.  
-        */
+        /* It seems odd that libpng lets you get gamma-corrected pixel
+           values, but not gamma-corrected transparency or background
+           values.  But as that is the case, we have to gamma-correct
+           the transparency values.
 
-        retval = 
-            color.r == gamma_correct(transColorP->red,   totalgamma) &&
-            color.g == gamma_correct(transColorP->green, totalgamma) &&
-            color.b == gamma_correct(transColorP->blue,  totalgamma);
+           Note that because we compare the gamma-corrected values and
+           there may be many-to-one mapping of uncorrected to corrected
+           values, more pixels may be transparent than what the user
+           intended.
+
+           We could fix this by not letting libpng gamma-correct the
+           pixels, and just do it ourselves.
+        */
+    
+        switch (pngxP->info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
+            break;
+        default:
+            retval = 
+                color.r == gamma_correct(transColorP->red,   totalgamma) &&
+                color.g == gamma_correct(transColorP->green, totalgamma) &&
+                color.b == gamma_correct(transColorP->blue,  totalgamma);
+        }
     } else 
         retval = FALSE;
 
@@ -509,37 +687,17 @@ isTransparentColor(pngcolor   const color,
 
 
 
-#define SIG_CHECK_SIZE 4
-
 static void
-read_sig_buf(FILE * const ifP) {
-
-    unsigned char sig_buf[SIG_CHECK_SIZE];
-    size_t bytesRead;
-
-    bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP);
-    if (bytesRead != SIG_CHECK_SIZE)
-        pm_error ("input file is empty or too short");
-
-    if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE)
-        != 0)
-        pm_error ("input file is not a PNG file");
-}
-
-
-
-static void
-setupGammaCorrection(png_struct * const png_ptr,
-                     png_info *   const info_ptr,
-                     float        const displaygamma,
-                     float *      const totalgammaP) {
+setupGammaCorrection(struct pngx * const pngxP,
+                     float         const displaygamma,
+                     float *       const totalgammaP) {
 
     if (displaygamma == -1.0)
         *totalgammaP = -1.0;
     else {
         float imageGamma;
-        if (info_ptr->valid & PNG_INFO_gAMA)
-            imageGamma = info_ptr->gamma;
+        if (pngxP->info_ptr->valid & PNG_INFO_gAMA)
+            imageGamma = pngxP->info_ptr->gamma;
         else {
             if (verbose)
                 pm_message("PNG doesn't specify image gamma.  Assuming 1.0");
@@ -553,12 +711,12 @@ setupGammaCorrection(png_struct * const png_ptr,
                            "display gamma %4.2f.  No conversion.",
                            imageGamma, displaygamma);
         } else {
-            png_set_gamma(png_ptr, displaygamma, imageGamma);
+            png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma);
             *totalgammaP = imageGamma * displaygamma;
             /* in case of gamma-corrections, sBIT's as in the
                PNG-file are not valid anymore 
             */
-            info_ptr->valid &= ~PNG_INFO_sBIT;
+            pngxP->info_ptr->valid &= ~PNG_INFO_sBIT;
             if (verbose)
                 pm_message("image gamma is %4.2f, "
                            "converted for display gamma of %4.2f",
@@ -582,8 +740,8 @@ paletteHasPartialTransparency(png_info * const info_ptr) {
             for (i = 0, foundGray = FALSE;
                  i < info_ptr->num_trans && !foundGray;
                  ++i) {
-                if (info_ptr->trans[i] != 0 &&
-                    info_ptr->trans[i] != maxval) {
+                if (info_ptr->TRANS_ALPHA[i] != 0 &&
+                    info_ptr->TRANS_ALPHA[i] != maxval) {
                     foundGray = TRUE;
                 }
             }
@@ -599,18 +757,19 @@ paletteHasPartialTransparency(png_info * const info_ptr) {
 
 
 static void
-setupSignificantBits(png_struct *        const png_ptr,
-                     png_info *          const info_ptr,
+setupSignificantBits(struct pngx *       const pngxP,
                      enum alpha_handling const alpha,
                      png_uint_16 *       const maxvalP,
-                     int *               const errorlevelP) {
+                     int *               const errorLevelP) {
 /*----------------------------------------------------------------------------
   Figure out what maxval would best express the information in the PNG
-  described by 'png_ptr' and 'info_ptr', with 'alpha' telling which
-  information in the PNG we care about (image or alpha mask).
+  described by *pngxP, with 'alpha' telling which information in the PNG we
+  care about (image or alpha mask).
 
   Return the result as *maxvalP.
 -----------------------------------------------------------------------------*/
+    png_info * const info_ptr = pngxP->info_ptr;
+
     /* Initial assumption of maxval */
     if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
         if (alpha == ALPHA_ONLY) {
@@ -654,7 +813,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                 unsigned int i;
                 trans_mix = TRUE;
                 for (i = 0; i < info_ptr->num_trans; ++i)
-                    if (info_ptr->trans[i] != 0 && info_ptr->trans[i] != 255) {
+                    if (info_ptr->TRANS_ALPHA[i] != 0 && info_ptr->TRANS_ALPHA[i] != 255) {
                         trans_mix = FALSE;
                         break;
                     }
@@ -675,7 +834,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                            "different bit depths for color channels");
                 pm_message("writing file with %d bit resolution",
                            info_ptr->bit_depth);
-                *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+                *errorLevelP = PNMTOPNG_WARNING_LEVEL;
             } else {
                 if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) &&
                     (info_ptr->sig_bit.red < 255)) {
@@ -697,7 +856,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                     if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
                          info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
                         (info_ptr->sig_bit.red < info_ptr->bit_depth)) {
-                        png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                        png_set_shift(pngxP->png_ptr, &(info_ptr->sig_bit));
                         *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
                         if (verbose)
                             pm_message("image has fewer significant bits, "
@@ -709,7 +868,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                              info_ptr->color_type ==
                                  PNG_COLOR_TYPE_GRAY_ALPHA) &&
                             (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                            png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                            png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit);
                             *maxvalP = (1l << info_ptr->sig_bit.gray) - 1;
                             if (verbose)
                                 pm_message("image has fewer significant bits, "
@@ -723,7 +882,7 @@ setupSignificantBits(png_struct *        const png_ptr,
             if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
                  info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && 
                 (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit);
                 if (verbose)
                     pm_message ("image has fewer significant bits, "
                                 "writing file with %d bits", 
@@ -739,22 +898,22 @@ setupSignificantBits(png_struct *        const png_ptr,
 
 
 static bool
-imageHasColor(png_info * const info_ptr) {
+imageHasColor(struct pngx * const pngxP) {
 
     bool retval;
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
 
         retval = FALSE;
-    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+    else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
         bool foundColor;
         unsigned int i;
             
         for (i = 0, foundColor = FALSE;
-             i < info_ptr->num_palette && !foundColor;
+             i < pngxP->info_ptr->num_palette && !foundColor;
              ++i) {
-            if (iscolor(info_ptr->palette[i]))
+            if (iscolor(pngxP->info_ptr->palette[i]))
                 foundColor = TRUE;
         }
         retval = foundColor;
@@ -767,14 +926,14 @@ imageHasColor(png_info * const info_ptr) {
 
 
 static void
-determineOutputType(png_info *          const info_ptr,
+determineOutputType(struct pngx *       const pngxP,
                     enum alpha_handling const alphaHandling,
                     pngcolor            const bgColor,
                     xelval              const maxval,
                     int *               const pnmTypeP) {
 
     if (alphaHandling != ALPHA_ONLY &&
-        (imageHasColor(info_ptr) || !isGrayscale(bgColor)))
+        (imageHasColor(pngxP) || !isGrayscale(bgColor)))
         *pnmTypeP = PPM_TYPE;
     else {
         if (maxval > 1)
@@ -787,11 +946,11 @@ determineOutputType(png_info *          const info_ptr,
 
 
 static void
-getBackgroundColor(png_info *        const info_ptr,
-                   const char *      const requestedColor,
-                   float             const totalgamma,
-                   xelval            const maxval,
-                   struct pngcolor * const bgColorP) {
+getBackgroundColor(struct pngx * const pngxP,
+                   const char *  const requestedColor,
+                   float         const totalgamma,
+                   xelval        const maxval,
+                   pngcolor *    const bgColorP) {
 /*----------------------------------------------------------------------------
    Figure out what the background color should be.  If the user requested
    a particular color ('requestedColor' not null), that's the one.
@@ -809,19 +968,19 @@ getBackgroundColor(png_info *        const info_ptr,
         bgColorP->g = PPM_GETG(backcolor);
         bgColorP->b = PPM_GETB(backcolor);
 
-    } else if (info_ptr->valid & PNG_INFO_bKGD) {
+    } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) {
         /* didn't manage to get libpng to work (bugs?) concerning background
            processing, therefore we do our own.
         */
-        switch (info_ptr->color_type) {
+        switch (pngxP->info_ptr->color_type) {
         case PNG_COLOR_TYPE_GRAY:
         case PNG_COLOR_TYPE_GRAY_ALPHA:
             bgColorP->r = bgColorP->g = bgColorP->b = 
-                gamma_correct(info_ptr->background.gray, totalgamma);
+                gamma_correct(pngxP->info_ptr->background.gray, totalgamma);
             break;
         case PNG_COLOR_TYPE_PALETTE: {
             png_color const rawBgcolor = 
-                info_ptr->palette[info_ptr->background.index];
+                pngxP->info_ptr->palette[pngxP->info_ptr->background.index];
             bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
             bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
             bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
@@ -829,7 +988,7 @@ getBackgroundColor(png_info *        const info_ptr,
         break;
         case PNG_COLOR_TYPE_RGB:
         case PNG_COLOR_TYPE_RGB_ALPHA: {
-            png_color_16 const rawBgcolor = info_ptr->background;
+            png_color_16 const rawBgcolor = pngxP->info_ptr->background;
             
             bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
             bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
@@ -845,115 +1004,159 @@ getBackgroundColor(png_info *        const info_ptr,
 
 
 static void
+warnNonsquarePixels(struct pngx * const pngxP,
+                    int *         const errorLevelP) {
+
+    if (pngxP->info_ptr->valid & PNG_INFO_pHYs) {
+        float const r =
+            (float)pngxP->info_ptr->x_pixels_per_unit /
+            pngxP->info_ptr->y_pixels_per_unit;
+
+        if (r != 1.0) {
+            pm_message ("warning - non-square pixels; "
+                        "to fix do a 'pamscale -%cscale %g'",
+                        r < 1.0 ? 'x' : 'y',
+                        r < 1.0 ? 1.0 / r : r );
+            *errorLevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+}
+
+
+
+#define GET_PNG_VAL(p) get_png_val(&(p), pngxP->info_ptr->bit_depth)
+
+
+
+static void
+makeXelRow(xel *               const xelrow,
+           xelval              const maxval,
+           int                 const pnmType,
+           struct pngx *       const pngxP,
+           const png_byte *    const pngRasterRow,
+           pngcolor            const bgColor,
+           enum alpha_handling const alphaHandling,
+           double              const totalgamma) {
+
+    const png_byte * pngPixelP;
+    unsigned int col;
+
+    pngPixelP = &pngRasterRow[0];  /* initial value */
+    for (col = 0; col < pngxP->info_ptr->width; ++col) {
+        switch (pngxP->info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY: {
+            pngcolor fgColor;
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
+                   isTransparentColor(fgColor, pngxP, totalgamma) ?
+                   0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_GRAY_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
+            png_color   const paletteColor = pngxP->info_ptr->palette[index];
+
+            pngcolor fgColor;
+
+            fgColor.r = paletteColor.red;
+            fgColor.g = paletteColor.green;
+            fgColor.b = paletteColor.blue;
+
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
+                   (pngxP->info_ptr->valid & PNG_INFO_tRNS) &&
+                   index < pngxP->info_ptr->num_trans ?
+                   pngxP->info_ptr->TRANS_ALPHA[index] : maxval);
+        }
+        break;
+                
+        case PNG_COLOR_TYPE_RGB: {
+            pngcolor fgColor;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
+                   isTransparentColor(fgColor, pngxP, totalgamma) ?
+                   0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha     = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha);
+        }
+        break;
+
+        default:
+            pm_error("unknown PNG color type: %d",
+                     pngxP->info_ptr->color_type);
+        }
+    }
+}
+
+
+
+static void
 writePnm(FILE *              const ofP,
          xelval              const maxval,
-         int                 const pnm_type,
-         png_info *          const info_ptr,
-         png_byte **         const png_image,
+         int                 const pnmType,
+         struct pngx *       const pngxP,
+         png_byte **         const pngRaster,
          pngcolor            const bgColor,
-         enum alpha_handling const alpha_handling,
+         enum alpha_handling const alphaHandling,
          double              const totalgamma) {
 /*----------------------------------------------------------------------------
    Write a PNM of either the image or the alpha mask, according to
-   'alpha_handling' that is in the PNG image described by 'info_ptr' and
-   png_image.
+   'alphaHandling' that is in the PNG image described by *pngxP and
+   pngRaster[][].
 
-   'pnm_type' and 'maxval' are of the output image.
+   'pnmType' and 'maxval' are of the output image.
 
    Use background color 'bgColor' in the output if the PNG is such that a
    background color is needed.
 -----------------------------------------------------------------------------*/
+    int const plainFalse = 0;
+
     xel * xelrow;
     unsigned int row;
 
     if (verbose)
-        pm_message ("writing a %s file (maxval=%u)",
-                    pnm_type == PBM_TYPE ? "PBM" :
-                    pnm_type == PGM_TYPE ? "PGM" :
-                    pnm_type == PPM_TYPE ? "PPM" :
-                    "UNKNOWN!", 
-                    maxval);
+        pm_message("writing a %s file (maxval=%u)",
+                   pnmType == PBM_TYPE ? "PBM" :
+                   pnmType == PGM_TYPE ? "PGM" :
+                   pnmType == PPM_TYPE ? "PPM" :
+                   "UNKNOWN!", 
+                   maxval);
     
-    xelrow = pnm_allocrow(info_ptr->width);
-
-    pnm_writepnminit(stdout, info_ptr->width, info_ptr->height, maxval,
-                     pnm_type, FALSE);
-
-    for (row = 0; row < info_ptr->height; ++row) {
-        png_byte * png_pixelP;
-        int col;
-
-        png_pixelP = &png_image[row][0];  /* initial value */
-        for (col = 0; col < info_ptr->width; ++col) {
-            switch (info_ptr->color_type) {
-            case PNG_COLOR_TYPE_GRAY: {
-                pngcolor fgColor;
-                fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
-                       ((info_ptr->valid & PNG_INFO_tRNS) &&
-                        (fgColor.r == 
-                         gamma_correct(info_ptr->trans_values.gray,
-                                       totalgamma))) ?
-                       0 : maxval);
-            }
-            break;
-
-            case PNG_COLOR_TYPE_GRAY_ALPHA: {
-                pngcolor fgColor;
-                png_uint_16 alpha;
-
-                fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP);
-                alpha = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha);
-            }
-            break;
+    xelrow = pnm_allocrow(pngxP->info_ptr->width);
 
-            case PNG_COLOR_TYPE_PALETTE: {
-                png_uint_16 const index        = get_png_val(png_pixelP);
-                png_color   const paletteColor = info_ptr->palette[index];
-
-                pngcolor fgColor;
-
-                fgColor.r = paletteColor.red;
-                fgColor.g = paletteColor.green;
-                fgColor.b = paletteColor.blue;
-
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
-                       (info_ptr->valid & PNG_INFO_tRNS) &&
-                       index < info_ptr->num_trans ?
-                       info_ptr->trans[index] : maxval);
-            }
-            break;
-                
-            case PNG_COLOR_TYPE_RGB: {
-                pngcolor fgColor;
-
-                fgColor.r = get_png_val(png_pixelP);
-                fgColor.g = get_png_val(png_pixelP);
-                fgColor.b = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
-                       isTransparentColor(fgColor, info_ptr, totalgamma) ?
-                       0 : maxval);
-            }
-            break;
+    pnm_writepnminit(stdout,
+                     pngxP->info_ptr->width, pngxP->info_ptr->height, maxval,
+                     pnmType, plainFalse);
 
-            case PNG_COLOR_TYPE_RGB_ALPHA: {
-                pngcolor fgColor;
-                png_uint_16 alpha;
+    for (row = 0; row < pngxP->info_ptr->height; ++row) {
+        makeXelRow(xelrow, maxval, pnmType, pngxP, pngRaster[row], bgColor,
+                   alphaHandling, totalgamma);
 
-                fgColor.r = get_png_val(png_pixelP);
-                fgColor.g = get_png_val(png_pixelP);
-                fgColor.b = get_png_val(png_pixelP);
-                alpha     = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha);
-            }
-            break;
-
-            default:
-                pm_error ("unknown PNG color type: %d", info_ptr->color_type);
-            }
-        }
-        pnm_writepnmrow(ofP, xelrow, info_ptr->width, maxval, pnm_type, FALSE);
+        pnm_writepnmrow(ofP, xelrow, pngxP->info_ptr->width, maxval,
+                        pnmType, plainFalse);
     }
     pnm_freerow (xelrow);
 }
@@ -961,154 +1164,86 @@ writePnm(FILE *              const ofP,
 
 
 static void 
-convertpng(FILE *             const ifp, 
-           FILE *             const tfp, 
+convertpng(FILE *             const ifP, 
+           FILE *             const tfP, 
            struct cmdlineInfo const cmdline,
-           int *              const errorlevelP) {
-
-    png_struct *png_ptr;
-    png_info *info_ptr;
-    png_byte **png_image;
-    int x, y;
-    int linesize;
-    int pnm_type;
+           int *              const errorLevelP) {
+
+    png_byte ** pngRaster;
+    int pnmType;
     pngcolor bgColor;
     float totalgamma;
+    jmp_buf jmpbuf;
+    struct pngx * pngxP;
 
-    *errorlevelP = 0;
-
-    read_sig_buf(ifp);
-
-    png_ptr = png_create_read_struct(
-        PNG_LIBPNG_VER_STRING,
-        &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
-    if (png_ptr == NULL)
-        pm_error("cannot allocate main libpng structure (png_ptr)");
-
-    info_ptr = png_create_info_struct (png_ptr);
-    if (info_ptr == NULL)
-        pm_error("cannot allocate LIBPNG structures");
+    *errorLevelP = 0;
 
-    if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf))
+    if (setjmp(jmpbuf))
         pm_error ("setjmp returns error condition");
 
-    png_init_io (png_ptr, ifp);
-    png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE);
-    png_read_info (png_ptr, info_ptr);
+    pngx_createRead(&pngxP, &jmpbuf);
 
-    MALLOCARRAY(png_image, info_ptr->height);
-    if (png_image == NULL) {
-        png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
-        pm_closer (ifp);
-        pm_error ("couldn't allocate space for image");
-    }
+    readPng(pngxP, ifP, &pngRaster);
 
-    if (info_ptr->bit_depth == 16)
-        linesize = 2 * info_ptr->width;
-    else
-        linesize = info_ptr->width;
+    if (verbose)
+        dumpPngInfo(pngxP);
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-        linesize *= 2;
-    else
-        if (info_ptr->color_type == PNG_COLOR_TYPE_RGB)
-            linesize *= 3;
-        else
-            if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-                linesize *= 4;
-
-    for (y = 0 ; y < info_ptr->height ; y++) {
-        png_image[y] = malloc (linesize);
-        if (png_image[y] == NULL) {
-            for (x = 0 ; x < y ; x++)
-                free (png_image[x]);
-            free (png_image);
-            png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
-            pm_closer (ifp);
-            pm_error ("couldn't allocate space for image");
-        }
-    }
+    if (cmdline.time)
+        showTime(pngxP);
+    if (tfP)
+        saveText(pngxP, tfP);
 
-    if (info_ptr->bit_depth < 8)
-        png_set_packing (png_ptr);
+    warnNonsquarePixels(pngxP, errorLevelP);
 
-    setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma);
+    setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma);
 
-    setupSignificantBits(png_ptr, info_ptr, cmdline.alpha,
-                         &maxval, errorlevelP);
+    setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP);
 
-    getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval,
+    getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval,
                        &bgColor);
 
-    png_read_image (png_ptr, png_image);
-    png_read_end (png_ptr, info_ptr);
+    determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pnmType);
 
-    if (verbose)
-        /* 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.
-    */
-        dump_png_info(info_ptr);
-
-    if (mtime)
-        show_time (info_ptr);
-    if (tfp)
-        save_text (info_ptr, tfp);
-
-    if (info_ptr->valid & PNG_INFO_pHYs) {
-        float r;
-        r = (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit;
-        if (r != 1.0) {
-            pm_message ("warning - non-square pixels; "
-                        "to fix do a 'pamscale -%cscale %g'",
-                        r < 1.0 ? 'x' : 'y',
-                        r < 1.0 ? 1.0 / r : r );
-            *errorlevelP = PNMTOPNG_WARNING_LEVEL;
-        }
-    }
-
-    determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval, &pnm_type);
-
-    writePnm(stdout, maxval, pnm_type, info_ptr, png_image, bgColor, 
+    writePnm(stdout, maxval, pnmType, pngxP, pngRaster, bgColor, 
              cmdline.alpha, totalgamma);
 
     fflush(stdout);
-    for (y = 0 ; y < info_ptr->height ; y++)
-        free (png_image[y]);
-    free (png_image);
-    png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
+
+    freePngRaster(pngRaster, pngxP);
+
+    pngx_destroy(pngxP);
 }
 
 
 
 int 
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
-    FILE *ifp, *tfp;
-    int errorlevel;
+    FILE * ifP;
+    FILE * tfP;
+    int errorLevel;
 
-    pnm_init (&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     verbose = cmdline.verbose;
-    mtime = cmdline.time;
 
-    ifp = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFilespec);
 
     if (cmdline.text)
-        tfp = pm_openw(cmdline.text);
+        tfP = pm_openw(cmdline.text);
     else
-        tfp = NULL;
+        tfP = NULL;
 
-    convertpng (ifp, tfp, cmdline, &errorlevel);
+    convertpng(ifP, tfP, cmdline, &errorLevel);
 
-    if (tfp)
-        pm_close(tfp);
+    if (tfP)
+        pm_close(tfP);
 
-    pm_close(ifp);
+    pm_close(ifP);
     pm_close(stdout);
 
-    return errorlevel;
+    return errorLevel;
 }
diff --git a/converter/other/pnmtoddif.c b/converter/other/pnmtoddif.c
index 962487f2..ae8c8524 100644
--- a/converter/other/pnmtoddif.c
+++ b/converter/other/pnmtoddif.c
@@ -1,14 +1,4 @@
 /*
- * $Log: pnmtoddif.c,v $
- * Revision 1.6  1993/01/25  08:14:06  neideck
- * Placed into public use form.
- *
- * Revision 1.5  1992/12/02  08:15:18  neideck
- *  Added RCS id.
- *
- */
-
-/*
  * Author:      Burkhard Neidecker-Lutz
  *              Digital CEC Karlsruhe
  *      neideck@nestvx.enet.dec.com 
@@ -31,6 +21,8 @@
 */
 
 #include <string.h>
+
+#include "mallocvar.h"
 #include "pnm.h"
 
 /* The structure we use to convey all sorts of "magic" data to the DDIF */
@@ -166,11 +158,7 @@ wr_int(unsigned char ** buffer, int val)
     } else {
         sign = val < 0 ? 0xff : 0x00;   /* Sign bits */
         length = 4;
-#ifdef __STDC__
         mask  = 0xffu << 24;
-#else
-        mask  = 0xff << 24;
-#endif
         while ((val & mask) == sign) {  /* Find the smallest representation */
             length--;
             mask >>= 8;
@@ -418,21 +406,181 @@ write_trailer(FILE * file)
 
 
 
-int main(int argc, char *argv[])
-{
+
+static void
+convertPbmRaster(FILE *          const ifP,
+                 int             const format,
+                 unsigned int    const cols,
+                 unsigned int    const rows,
+                 FILE *          const ofP,
+                 unsigned int    const bytesPerLine,
+                 unsigned char * const data) {
+                 
+    bit * const pixels = pbm_allocrow(cols);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        unsigned int k;
+        unsigned int mask;
+        unsigned char * p;
+        size_t bytesWritten;
+
+        pbm_readpbmrow(ifP, pixels, cols, format);
+
+        mask = 0x00;
+        p = &data[0];
+        for (col = 0, k = 0; col < cols; ++col) {
+            if (pixels[col] == PBM_BLACK)
+                mask |= 1 << k;
+            if (k == 7) {
+                *p++ = mask;
+                mask = 0x00;
+                k = 0;
+            } else
+                ++k;
+        }
+        if (k != 7)
+            /* Flush the rest of the column */
+            *p = mask;
+
+        bytesWritten =  fwrite(data, 1, bytesPerLine, ofP);
+        if (bytesWritten != bytesPerLine)
+            pm_error("File write error on Row %u", row);
+    }
+
+    pbm_freerow(pixels);
+}
+
+
+
+static void
+convertPgmRaster(FILE *          const ifP,
+                 int             const format,
+                 xelval          const maxval,
+                 unsigned int    const cols,
+                 unsigned int    const rows,
+                 FILE *          const ofP,
+                 unsigned int    const bytesPerLine,
+                 unsigned char * const data) {
+
+    gray * const pixels = pgm_allocrow(cols);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned char * p;
+        unsigned int col;
+        size_t bytesWritten;
+
+        p = &data[0];
+
+        pgm_readpgmrow(ifP, pixels, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col)
+            *p++ = (unsigned char) pixels[col];
+
+        bytesWritten = fwrite(data, 1, bytesPerLine, ofP);
+        if (bytesWritten != bytesPerLine)
+            pm_error("File write error on Row %u", row);
+    }
+    pgm_freerow(pixels);
+}
+
+
+
+
+static void
+convertPpmRaster(FILE *          const ifP,
+                 int             const format,
+                 xelval          const maxval,
+                 unsigned int    const cols,
+                 unsigned int    const rows,
+                 FILE *          const ofP,
+                 unsigned int    const bytesPerLine,
+                 unsigned char * const data) {
+
+    pixel * const pixels = ppm_allocrow(cols);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned char * p;
+        unsigned int col;
+        size_t bytesWritten;
+
+        p = &data[0];
+
+        ppm_readppmrow(ifP, pixels, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            *p++ = PPM_GETR(pixels[col]);
+            *p++ = PPM_GETG(pixels[col]);
+            *p++ = PPM_GETB(pixels[col]);
+        }
+        bytesWritten =  fwrite(data, 1, bytesPerLine, ofP);
+        if (bytesWritten != bytesPerLine)
+            pm_error("File write error on Row %u", row);
+    }
+    ppm_freerow(pixels);
+}
+
+
+
+static void
+convertRaster(FILE *       const ifP,
+              int          const format,
+              xelval       const maxval,
+              unsigned int const cols,
+              unsigned int const rows,
+              FILE *       const ofP,
+              unsigned int const bytesPerLine) {
+
+    unsigned char * data;
+    unsigned char * p;
+
+    MALLOCARRAY(data, bytesPerLine);
+
+    if (data == NULL)
+        pm_error("Couldn't allocate %u-byte line buffer", bytesPerLine);
+
+    p = data;  /* initial value */
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        convertPbmRaster(ifP, format, cols, rows, ofP, bytesPerLine, data);
+        break;
+    case PGM_TYPE:
+        convertPgmRaster(ifP, format, maxval, cols, rows, ofP, bytesPerLine,
+                         data);
+        break;
+    case PPM_TYPE:
+        convertPpmRaster(ifP, format, maxval, cols, rows, ofP, bytesPerLine,
+                         data);
+        break;
+    default:
+        pm_error("INTERNAL ERROR: impossible format value");
+    }
+
+    free(data);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
     FILE           *ifd;
-    FILE       *ofd;
+    FILE           *ofd;
     int             rows, cols;
     xelval          maxval;
     int             format;
     const char     * const usage = "[-resolution x y] [pnmfile [ddiffile]]";
-    int             i, j;
     char           *outfile;
     int       argn;
     int hor_resolution = 75;
     int ver_resolution = 75;
     imageparams ip;
-    unsigned char  *data, *p;
 
     pnm_init(&argc, argv);
 
@@ -516,90 +664,10 @@ int main(int argc, char *argv[])
         exit(1);
     }
 
-    if (!(p = data = (unsigned char*)  malloc(ip.bytes_per_line))) {
-        perror("allocating line buffer");
-        exit(1);
-    }
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PBM_TYPE:
-    {
-        bit            *pixels;
-        int             mask;
-        int             k;
-
-        pixels = pbm_allocrow(cols);
-
-        for (i = 0; i < rows; i++) {
-            pbm_readpbmrow(ifd, pixels, cols, format);
-            mask = 0;
-            p = data;
-            for (j = 0, k = 0; j < cols; j++) {
-                if (pixels[j] == PBM_BLACK) {
-                    mask |= 1 << k;
-                }
-                if (k == 7) {
-                    *p++ = mask;
-                    mask = 0;
-                    k = 0;
-                } else {
-                    k++;
-                }
-            }
-            if (k != 7) {       /* Flush the rest of the column */
-                *p = mask;
-            }
-            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
-                perror("Writing image data\n");
-                exit(1);
-            }
-        }
-    }
-    break;
-    case PGM_TYPE:
-    {
-        gray          *pixels = pgm_allocrow(cols);
-
-        for (i = 0; i < rows; i++) {
-            p = data;
-            pgm_readpgmrow(ifd, pixels, cols, maxval, format);
-            for (j = 0; j < cols; j++) {
-                *p++ = (unsigned char) pixels[j];
-            }
-            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
-                perror("Writing image data\n");
-                exit(1);
-            }
-        }
-        pgm_freerow(pixels);
-    }
-    break;
-    case PPM_TYPE:
-    {
-        pixel          *pixels = ppm_allocrow(cols);
-
-        for (i = 0; i < rows; i++) {
-            p = data;
-            ppm_readppmrow(ifd, pixels, cols, maxval, format);
-            for (j = 0; j < cols; j++) {
-                *p++ = PPM_GETR(pixels[j]);
-                *p++ = PPM_GETG(pixels[j]);
-                *p++ = PPM_GETB(pixels[j]);
-            }
-            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
-                perror("Writing image data\n");
-                exit(1);
-            }
-        }
-        ppm_freerow(pixels);
-    }
-    break;
-    }
+    convertRaster(ifd, format, maxval, cols, rows, ofd, ip.bytes_per_line);
 
     pm_close(ifd);
 
-    free(data);
-
     if (!write_trailer(ofd)) {
         perror("Writing trailer");
         exit(1);
diff --git a/converter/other/pnmtojpeg.c b/converter/other/pnmtojpeg.c
index 9a247633..198aa156 100644
--- a/converter/other/pnmtojpeg.c
+++ b/converter/other/pnmtojpeg.c
@@ -29,6 +29,8 @@
    itself, but doesn't.
 */
 #include <jpeglib.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -330,23 +332,6 @@ parseCommandLine(const int argc, char ** argv,
 }
 
 
-static void
-compute_rescaling_array(JSAMPLE ** const rescale_p, const pixval maxval,
-                        const struct jpeg_compress_struct cinfo);
-static void
-convert_scanlines(struct jpeg_compress_struct * const cinfo_p, FILE * const input_file,
-                  const pixval maxval, const int input_fmt,
-                  JSAMPLE xlate_table[]);
-
-static boolean read_quant_tables (j_compress_ptr cinfo, char * filename,
-                                  int scale_factor, boolean force_baseline);
-
-static boolean read_scan_script (j_compress_ptr cinfo, char * filename);
-
-static boolean set_quant_slots (j_compress_ptr cinfo, char *arg);
-
-static boolean set_sample_factors (j_compress_ptr cinfo, char *arg);
-
 
 static void
 report_compressor(const struct jpeg_compress_struct cinfo) {
@@ -423,6 +408,370 @@ setup_jpeg_density(struct jpeg_compress_struct * const cinfoP,
 
 
 
+/*----------------------------------------------------------------------------
+   The functions below here are essentially the file rdswitch.c from
+   the JPEG library.  They perform the functions specifed by the following
+   pnmtojpeg options:
+
+   -qtables file          Read quantization tables from text file
+   -scans file            Read scan script from text file
+   -qslots N[,N,...]      Set component quantization table selectors
+   -sample HxV[,HxV,...]  Set component sampling factors
+-----------------------------------------------------------------------------*/
+
+static int
+text_getc (FILE * file)
+/* Read next char, skipping over any comments (# to end of line) */
+/* A comment/newline sequence is returned as a newline */
+{
+    register int ch;
+  
+    ch = getc(file);
+    if (ch == '#') {
+        do {
+            ch = getc(file);
+        } while (ch != '\n' && ch != EOF);
+    }
+    return ch;
+}
+
+
+static boolean
+readTextInteger(FILE * const fileP,
+                long * const resultP,
+                int *  const termcharP) {
+/*----------------------------------------------------------------------------
+   Read the next unsigned decimal integer from file 'fileP', skipping
+   white space as necessary.  Return it as *resultP.
+
+   Also read one character after the integer and return it as *termcharP.
+
+   If there is no character after the integer, return *termcharP == EOF.
+
+   Iff the next thing in the file is not a valid unsigned decimal integer,
+   return FALSE.
+-----------------------------------------------------------------------------*/
+    int ch;
+    boolean retval;
+  
+    /* Skip any leading whitespace, detect EOF */
+    do {
+        ch = text_getc(fileP);
+    } while (isspace(ch));
+  
+    if (!isdigit(ch))
+        retval = FALSE;
+    else {
+        long val;
+        val = ch - '0';  /* initial value */
+        while ((ch = text_getc(fileP)) != EOF) {
+            if (! isdigit(ch))
+                break;
+            val *= 10;
+            val += ch - '0';
+        }
+        *resultP = val;
+        retval = TRUE;
+    }
+    *termcharP = ch;
+    return retval;
+}
+
+
+static boolean
+read_scan_integer (FILE * file, long * result, int * termchar)
+/* Variant of readTextInteger that always looks for a non-space termchar;
+ * this simplifies parsing of punctuation in scan scripts.
+ */
+{
+    register int ch;
+
+    if (! readTextInteger(file, result, termchar))
+        return FALSE;
+    ch = *termchar;
+    while (ch != EOF && isspace(ch))
+        ch = text_getc(file);
+    if (isdigit(ch)) {		/* oops, put it back */
+        if (ungetc(ch, file) == EOF)
+            return FALSE;
+        ch = ' ';
+    } else {
+        /* Any separators other than ';' and ':' are ignored;
+         * this allows user to insert commas, etc, if desired.
+         */
+        if (ch != EOF && ch != ';' && ch != ':')
+            ch = ' ';
+    }
+    *termchar = ch;
+    return TRUE;
+}
+
+
+
+static boolean
+read_scan_script(j_compress_ptr const cinfo,
+                 const char *   const filename) {
+/*----------------------------------------------------------------------------
+  Read a scan script from the specified text file.
+  Each entry in the file defines one scan to be emitted.
+  Entries are separated by semicolons ';'.
+  An entry contains one to four component indexes,
+  optionally followed by a colon ':' and four progressive-JPEG parameters.
+  The component indexes denote which component(s) are to be transmitted
+  in the current scan.  The first component has index 0.
+  Sequential JPEG is used if the progressive-JPEG parameters are omitted.
+  The file is free format text: any whitespace may appear between numbers
+  and the ':' and ';' punctuation marks.  Also, other punctuation (such
+  as commas or dashes) can be placed between numbers if desired.
+  Comments preceded by '#' may be included in the file.
+  Note: we do very little validity checking here;
+  jcmaster.c will validate the script parameters.
+-----------------------------------------------------------------------------*/
+    FILE * fp;
+    unsigned int nscans;
+    unsigned int ncomps;
+    int termchar;
+    long val;
+#define MAX_SCANS  100      /* quite arbitrary limit */
+    jpeg_scan_info scans[MAX_SCANS];
+
+    fp = fopen(filename, "r");
+    if (fp == NULL) {
+        pm_message("Can't open scan definition file %s", filename);
+        return FALSE;
+    }
+    nscans = 0;
+
+    while (read_scan_integer(fp, &val, &termchar)) {
+        ++nscans;  /* We got another scan */
+        if (nscans > MAX_SCANS) {
+            pm_message("Too many scans defined in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+        scans[nscans-1].component_index[0] = (int) val;
+        ncomps = 1;
+        while (termchar == ' ') {
+            if (ncomps >= MAX_COMPS_IN_SCAN) {
+                pm_message("Too many components in one scan in file %s", 
+                           filename);
+                fclose(fp);
+                return FALSE;
+            }
+            if (! read_scan_integer(fp, &val, &termchar))
+                goto bogus;
+            scans[nscans-1].component_index[ncomps] = (int) val;
+            ++ncomps;
+        }
+        scans[nscans-1].comps_in_scan = ncomps;
+        if (termchar == ':') {
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Ss = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Se = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Ah = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar))
+                goto bogus;
+            scans[nscans-1].Al = (int) val;
+        } else {
+            /* set non-progressive parameters */
+            scans[nscans-1].Ss = 0;
+            scans[nscans-1].Se = DCTSIZE2-1;
+            scans[nscans-1].Ah = 0;
+            scans[nscans-1].Al = 0;
+        }
+        if (termchar != ';' && termchar != EOF) {
+        bogus:
+            pm_message("Invalid scan entry format in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+    }
+
+    if (termchar != EOF) {
+        pm_message("Non-numeric data in file %s", filename);
+        fclose(fp);
+        return FALSE;
+    }
+
+    if (nscans > 0) {
+        /* Stash completed scan list in cinfo structure.  NOTE: in
+           this program, JPOOL_IMAGE is the right lifetime for this
+           data, but if you want to compress multiple images you'd
+           want JPOOL_PERMANENT.  
+        */
+        const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info);
+        jpeg_scan_info * const scan_info = 
+            (jpeg_scan_info *)
+            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+                                        scan_info_size);
+        memcpy(scan_info, scans, scan_info_size);
+        cinfo->scan_info = scan_info;
+        cinfo->num_scans = nscans;
+    }
+
+    fclose(fp);
+    return TRUE;
+}
+
+
+
+static boolean
+read_quant_tables (j_compress_ptr cinfo, char * filename,
+                   int scale_factor, boolean force_baseline)
+/* Read a set of quantization tables from the specified file.
+ * The file is plain ASCII text: decimal numbers with whitespace between.
+ * Comments preceded by '#' may be included in the file.
+ * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
+ * The tables are implicitly numbered 0,1,etc.
+ * NOTE: does not affect the qslots mapping, which will default to selecting
+ * table 0 for luminance (or primary) components, 1 for chrominance components.
+ * You must use -qslots if you want a different component->table mapping.
+ */
+{
+    FILE * fp;
+    boolean retval;
+
+    fp = fopen(filename, "rb");
+    if (fp == NULL) {
+        pm_message("Can't open table file %s", filename);
+        retval = FALSE;
+    } else {
+        boolean eof, error;
+        unsigned int tblno;
+
+        for (tblno = 0, eof = FALSE, error = FALSE; !eof && !error; ++tblno) {
+            long val;
+            int termchar;
+            boolean gotOne;
+
+            gotOne = readTextInteger(fp, &val, &termchar);
+            if (gotOne) {
+                /* read 1st element of table */
+                if (tblno >= NUM_QUANT_TBLS) {
+                    pm_message("Too many tables in file %s", filename);
+                    error = TRUE;
+                } else { 
+                    unsigned int table[DCTSIZE2];
+                    unsigned int i;
+
+                    table[0] = (unsigned int) val;
+                    for (i = 1; i < DCTSIZE2 && !error; ++i) {
+                        if (! readTextInteger(fp, &val, &termchar)) {
+                            pm_message("Invalid table data in file %s",
+                                       filename);
+                            error = TRUE;
+                        } else
+                            table[i] = (unsigned int) val;
+                    }
+                    if (!error)
+                        jpeg_add_quant_table(
+                            cinfo, tblno, table, scale_factor, force_baseline);
+                }
+            } else {
+                if (termchar == EOF)
+                    eof = TRUE;
+                else {
+                    pm_message("Non-numeric data in file %s", filename);
+                    error = TRUE;
+                }
+            }
+        }
+
+        fclose(fp);
+        retval = !error;
+    }
+        
+    return retval;
+}
+
+
+
+static boolean
+set_quant_slots (j_compress_ptr cinfo, char *arg)
+/* Process a quantization-table-selectors parameter string, of the form
+ *     N[,N,...]
+ * If there are more components than parameters, the last value is replicated.
+ */
+{
+    int val = 0;			/* default table # */
+    int ci;
+    char ch;
+
+    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
+        if (*arg) {
+            ch = ',';			/* if not set by sscanf, will be ',' */
+            if (sscanf(arg, "%d%c", &val, &ch) < 1)
+                return FALSE;
+            if (ch != ',')		/* syntax check */
+                return FALSE;
+            if (val < 0 || val >= NUM_QUANT_TBLS) {
+                pm_message("Invalid quantization table number: %d.  "
+                           "JPEG quantization tables are numbered 0..%d",
+                           val, NUM_QUANT_TBLS - 1);
+                return FALSE;
+            }
+            cinfo->comp_info[ci].quant_tbl_no = val;
+            while (*arg && *arg++ != ',') 
+                /* advance to next segment of arg string */
+                ;
+        } else {
+            /* reached end of parameter, set remaining components to last tbl*/
+            cinfo->comp_info[ci].quant_tbl_no = val;
+        }
+    }
+    return TRUE;
+}
+
+
+static boolean
+set_sample_factors (j_compress_ptr cinfo, char *arg)
+/* Process a sample-factors parameter string, of the form
+ *     HxV[,HxV,...]
+ * If there are more components than parameters, "1x1" is assumed for the rest.
+ */
+{
+    int ci, val1, val2;
+    char ch1, ch2;
+
+    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
+        if (*arg) {
+            ch2 = ',';		/* if not set by sscanf, will be ',' */
+            if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
+                return FALSE;
+            if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
+                return FALSE;
+            if (val1 <= 0 || val1 > 4) {
+                pm_message("Invalid sampling factor: %d.  " 
+                           "JPEG sampling factors must be 1..4", val1);
+                return FALSE;
+            }
+            if (val2 <= 0 || val2 > 4) {
+                pm_message("Invalid sampling factor: %d.  "
+                           "JPEG sampling factors must be 1..4", val2);
+                return FALSE;
+            }
+            cinfo->comp_info[ci].h_samp_factor = val1;
+            cinfo->comp_info[ci].v_samp_factor = val2;
+            while (*arg && *arg++ != ',') 
+                /* advance to next segment of arg string */
+                ;
+        } else {
+            /* reached end of parameter, set remaining components 
+               to 1x1 sampling */
+            cinfo->comp_info[ci].h_samp_factor = 1;
+            cinfo->comp_info[ci].v_samp_factor = 1;
+        }
+    }
+    return TRUE;
+}
+
+
+
 static void
 setup_jpeg(struct jpeg_compress_struct * const cinfoP,
            struct jpeg_error_mgr       * const jerrP,
@@ -686,354 +1035,21 @@ convert_scanlines(struct jpeg_compress_struct * const cinfo_p,
   }
 
   pnm_freerow(pnm_buffer);
-  /* Dont' worry about the compressor input buffer; it gets freed 
+  /* Don't worry about the compressor input buffer; it gets freed 
      automatically
   */
-
-}
-
-/*----------------------------------------------------------------------------
-   The functions below here are essentially the file rdswitch.c from
-   the JPEG library.  They perform the functions specifed by the following
-   pnmtojpeg options:
-
-   -qtables file          Read quantization tables from text file
-   -scans file            Read scan script from text file
-   -qslots N[,N,...]      Set component quantization table selectors
-   -sample HxV[,HxV,...]  Set component sampling factors
------------------------------------------------------------------------------*/
-
-static int
-text_getc (FILE * file)
-/* Read next char, skipping over any comments (# to end of line) */
-/* A comment/newline sequence is returned as a newline */
-{
-    register int ch;
-  
-    ch = getc(file);
-    if (ch == '#') {
-        do {
-            ch = getc(file);
-        } while (ch != '\n' && ch != EOF);
-    }
-    return ch;
-}
-
-
-static boolean
-read_text_integer (FILE * file, long * result, int * termchar)
-/* Read an unsigned decimal integer from a file, store it in result */
-/* Reads one trailing character after the integer; returns it in termchar */
-{
-    register int ch;
-    register long val;
-  
-    /* Skip any leading whitespace, detect EOF */
-    do {
-        ch = text_getc(file);
-        if (ch == EOF) {
-            *termchar = ch;
-            return FALSE;
-        }
-    } while (isspace(ch));
-  
-    if (! isdigit(ch)) {
-        *termchar = ch;
-        return FALSE;
-    }
-
-    val = ch - '0';
-    while ((ch = text_getc(file)) != EOF) {
-        if (! isdigit(ch))
-            break;
-        val *= 10;
-        val += ch - '0';
-    }
-    *result = val;
-    *termchar = ch;
-    return TRUE;
-}
-
-
-static boolean
-read_quant_tables (j_compress_ptr cinfo, char * filename,
-                   int scale_factor, boolean force_baseline)
-/* Read a set of quantization tables from the specified file.
- * The file is plain ASCII text: decimal numbers with whitespace between.
- * Comments preceded by '#' may be included in the file.
- * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
- * The tables are implicitly numbered 0,1,etc.
- * NOTE: does not affect the qslots mapping, which will default to selecting
- * table 0 for luminance (or primary) components, 1 for chrominance components.
- * You must use -qslots if you want a different component->table mapping.
- */
-{
-    FILE * fp;
-    int tblno, i, termchar;
-    long val;
-    unsigned int table[DCTSIZE2];
-
-    if ((fp = fopen(filename, "rb")) == NULL) {
-        pm_message("Can't open table file %s", filename);
-        return FALSE;
-    }
-    tblno = 0;
-
-    while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */
-        if (tblno >= NUM_QUANT_TBLS) {
-            pm_message("Too many tables in file %s", filename);
-            fclose(fp);
-            return FALSE;
-        }
-        table[0] = (unsigned int) val;
-        for (i = 1; i < DCTSIZE2; i++) {
-            if (! read_text_integer(fp, &val, &termchar)) {
-                pm_message("Invalid table data in file %s", filename);
-                fclose(fp);
-                return FALSE;
-            }
-            table[i] = (unsigned int) val;
-        }
-        jpeg_add_quant_table(cinfo, tblno, table, scale_factor, 
-                             force_baseline);
-        tblno++;
-    }
-
-    if (termchar != EOF) {
-        pm_message("Non-numeric data in file %s", filename);
-        fclose(fp);
-        return FALSE;
-    }
-
-    fclose(fp);
-    return TRUE;
-}
-
-
-static boolean
-read_scan_integer (FILE * file, long * result, int * termchar)
-/* Variant of read_text_integer that always looks for a non-space termchar;
- * this simplifies parsing of punctuation in scan scripts.
- */
-{
-    register int ch;
-
-    if (! read_text_integer(file, result, termchar))
-        return FALSE;
-    ch = *termchar;
-    while (ch != EOF && isspace(ch))
-        ch = text_getc(file);
-    if (isdigit(ch)) {		/* oops, put it back */
-        if (ungetc(ch, file) == EOF)
-            return FALSE;
-        ch = ' ';
-    } else {
-        /* Any separators other than ';' and ':' are ignored;
-         * this allows user to insert commas, etc, if desired.
-         */
-        if (ch != EOF && ch != ';' && ch != ':')
-            ch = ' ';
-    }
-    *termchar = ch;
-    return TRUE;
-}
-
-
-boolean
-read_scan_script (j_compress_ptr cinfo, char * filename)
-/* Read a scan script from the specified text file.
- * Each entry in the file defines one scan to be emitted.
- * Entries are separated by semicolons ';'.
- * An entry contains one to four component indexes,
- * optionally followed by a colon ':' and four progressive-JPEG parameters.
- * The component indexes denote which component(s) are to be transmitted
- * in the current scan.  The first component has index 0.
- * Sequential JPEG is used if the progressive-JPEG parameters are omitted.
- * The file is free format text: any whitespace may appear between numbers
- * and the ':' and ';' punctuation marks.  Also, other punctuation (such
- * as commas or dashes) can be placed between numbers if desired.
- * Comments preceded by '#' may be included in the file.
- * Note: we do very little validity checking here;
- * jcmaster.c will validate the script parameters.
- */
-{
-    FILE * fp;
-    int nscans, ncomps, termchar;
-    long val;
-#define MAX_SCANS  100      /* quite arbitrary limit */
-    jpeg_scan_info scans[MAX_SCANS];
-
-    if ((fp = fopen(filename, "r")) == NULL) {
-        pm_message("Can't open scan definition file %s", filename);
-        return FALSE;
-    }
-    nscans = 0;
-
-    while (read_scan_integer(fp, &val, &termchar)) {
-        nscans++;  /* We got another scan */
-        if (nscans > MAX_SCANS) {
-            pm_message("Too many scans defined in file %s", filename);
-            fclose(fp);
-            return FALSE;
-        }
-        scans[nscans-1].component_index[0] = (int) val;
-        ncomps = 1;
-        while (termchar == ' ') {
-            if (ncomps >= MAX_COMPS_IN_SCAN) {
-                pm_message("Too many components in one scan in file %s", 
-                           filename);
-                fclose(fp);
-                return FALSE;
-            }
-            if (! read_scan_integer(fp, &val, &termchar))
-                goto bogus;
-            scans[nscans-1].component_index[ncomps] = (int) val;
-            ncomps++;
-        }
-        scans[nscans-1].comps_in_scan = ncomps;
-        if (termchar == ':') {
-            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
-                goto bogus;
-            scans[nscans-1].Ss = (int) val;
-            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
-                goto bogus;
-            scans[nscans-1].Se = (int) val;
-            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
-                goto bogus;
-            scans[nscans-1].Ah = (int) val;
-            if (! read_scan_integer(fp, &val, &termchar))
-                goto bogus;
-            scans[nscans-1].Al = (int) val;
-        } else {
-            /* set non-progressive parameters */
-            scans[nscans-1].Ss = 0;
-            scans[nscans-1].Se = DCTSIZE2-1;
-            scans[nscans-1].Ah = 0;
-            scans[nscans-1].Al = 0;
-        }
-        if (termchar != ';' && termchar != EOF) {
-        bogus:
-            pm_message("Invalid scan entry format in file %s", filename);
-            fclose(fp);
-            return FALSE;
-        }
-    }
-
-    if (termchar != EOF) {
-        pm_message("Non-numeric data in file %s", filename);
-        fclose(fp);
-        return FALSE;
-    }
-
-    if (nscans > 0) {
-        /* Stash completed scan list in cinfo structure.  NOTE: in
-         * this program, JPOOL_IMAGE is the right lifetime for this
-         * data, but if you want to compress multiple images you'd
-         * want JPOOL_PERMANENT.  
-         */
-        const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info);
-        jpeg_scan_info * const scan_info = 
-            (jpeg_scan_info *)
-            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
-                                        scan_info_size);
-        memcpy(scan_info, scans, scan_info_size);
-        cinfo->scan_info = scan_info;
-        cinfo->num_scans = nscans;
-    }
-
-    fclose(fp);
-    return TRUE;
-}
-
-
-static boolean
-set_quant_slots (j_compress_ptr cinfo, char *arg)
-/* Process a quantization-table-selectors parameter string, of the form
- *     N[,N,...]
- * If there are more components than parameters, the last value is replicated.
- */
-{
-    int val = 0;			/* default table # */
-    int ci;
-    char ch;
-
-    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
-        if (*arg) {
-            ch = ',';			/* if not set by sscanf, will be ',' */
-            if (sscanf(arg, "%d%c", &val, &ch) < 1)
-                return FALSE;
-            if (ch != ',')		/* syntax check */
-                return FALSE;
-            if (val < 0 || val >= NUM_QUANT_TBLS) {
-                pm_message("Invalid quantization table number: %d.  "
-                           "JPEG quantization tables are numbered 0..%d",
-                           val, NUM_QUANT_TBLS - 1);
-                return FALSE;
-            }
-            cinfo->comp_info[ci].quant_tbl_no = val;
-            while (*arg && *arg++ != ',') 
-                /* advance to next segment of arg string */
-                ;
-        } else {
-            /* reached end of parameter, set remaining components to last tbl*/
-            cinfo->comp_info[ci].quant_tbl_no = val;
-        }
-    }
-    return TRUE;
-}
-
-
-static boolean
-set_sample_factors (j_compress_ptr cinfo, char *arg)
-/* Process a sample-factors parameter string, of the form
- *     HxV[,HxV,...]
- * If there are more components than parameters, "1x1" is assumed for the rest.
- */
-{
-    int ci, val1, val2;
-    char ch1, ch2;
-
-    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
-        if (*arg) {
-            ch2 = ',';		/* if not set by sscanf, will be ',' */
-            if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
-                return FALSE;
-            if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
-                return FALSE;
-            if (val1 <= 0 || val1 > 4) {
-                pm_message("Invalid sampling factor: %d.  " 
-                           "JPEG sampling factors must be 1..4", val1);
-                return FALSE;
-            }
-            if (val2 <= 0 || val2 > 4) {
-                pm_message("Invalid sampling factor: %d.  "
-                           "JPEG sampling factors must be 1..4", val2);
-                return FALSE;
-            }
-            cinfo->comp_info[ci].h_samp_factor = val1;
-            cinfo->comp_info[ci].v_samp_factor = val2;
-            while (*arg && *arg++ != ',') 
-                /* advance to next segment of arg string */
-                ;
-        } else {
-            /* reached end of parameter, set remaining components 
-               to 1x1 sampling */
-            cinfo->comp_info[ci].h_samp_factor = 1;
-            cinfo->comp_info[ci].v_samp_factor = 1;
-        }
-    }
-    return TRUE;
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int     argc,
+     char ** argv) {
 
     struct cmdlineInfo cmdline;
     struct jpeg_compress_struct cinfo;
     struct jpeg_error_mgr jerr;
-    FILE *input_file;
+    FILE * input_file;
     FILE * output_file;
     int height;  
         /* height of the input image in rows, as specified by its header */
@@ -1089,7 +1105,7 @@ main(int argc, char ** argv) {
 
     /* Close files, if we opened them */
     if (input_file != stdin)
-        fclose(input_file);
+        pm_close(input_file);
 
     /* Program may have exited with non-zero completion code via
        various function calls above. 
diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile
index 4a8fa02a..7f99f95a 100644
--- a/converter/other/pnmtopalm/Makefile
+++ b/converter/other/pnmtopalm/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other/pnmtopalm
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 BINARIES = palmtopnm pnmtopalm
 SCRIPTS =
@@ -16,7 +16,7 @@ DATAFILES = palmcolor8.map palmgray1.map palmgray2.map palmgray4.map
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB))
 
@@ -24,8 +24,9 @@ $(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $< palmcolormap.o $(LIBOPTS) \
 	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
-gen_palm_colormap : $(SUBDIR)/gen_palm_colormap.c palmcolormap.o
-	$(CC) $(INCLUDES) $(CFLAGS) -o $@ $< palmcolormap.o \
+gen_palm_colormap : % : %.c palmcolormap.o
+	$(CC) -I importinc $(CPPFLAGS) $(CFLAGS) -o $@ \
+	  $< palmcolormap.o \
 	  $(LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(LADD)
 
 
diff --git a/converter/other/pnmtopalm/gen_palm_colormap.c b/converter/other/pnmtopalm/gen_palm_colormap.c
index 4b65e631..c7172c6b 100644
--- a/converter/other/pnmtopalm/gen_palm_colormap.c
+++ b/converter/other/pnmtopalm/gen_palm_colormap.c
@@ -1,24 +1,46 @@
 /* gen_palm_colormap.c - generate a ppm file containing the default Palm colormap
  *
- * Bill Janssen  <bill@janssen.org>
+ * Based on an earlier version by Bill Janssen  <bill@janssen.org>
  */
 
-#include "pnm.h"
+#include "ppm.h"
+#include "pm_c_util.h"
 
 #include "palm.h"
 
-int main( int argc, char **argv ) {
+int
+main(int     argc,
+     char ** argv) {
 
-  int i;
-  Color_s current;
-  Colormap default_map = palmcolor_build_default_8bit_colormap ();
+    Colormap defaultMap;
+    unsigned int i;
+    pixel pix;
+    
+    defaultMap = palmcolor_build_default_8bit_colormap();
+    qsort (defaultMap->color_entries, defaultMap->ncolors,
+           sizeof(Color_s), palmcolor_compare_indices);
 
-  printf("P3\n%d 1\n255\n", default_map->ncolors);
-  for (i = 0;  i < default_map->ncolors;  i++) {
-    current = default_map->color_entries[i];
-    printf ("%d %d %d\n", (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF));
-    /* printf ("%x:  %d %d %d\n", (current & 0xFF000000) >> 24, (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF)); */
-  };
-  return 0;
+    ppm_writeppminit(stdout, 256, 1, 255, TRUE);
+
+    for (i = 0; i < defaultMap->ncolors; ++i) {
+        Color_s const current = defaultMap->color_entries[i];
+
+        PPM_ASSIGN(pix,
+                   (current >> 16) & 0xff,
+                   (current >>  8) & 0xff,
+                   (current >>  0) & 0xff);
+
+        ppm_writeppmrow(stdout, &pix, 1, 255, TRUE);
+    }
+
+    /* palmcolor_build_default_8bit_colormap() builds a map of the 231 default
+     * palm colors and 1 extra black pixel. Add another 24 extra black pixels
+     * as per spec. */
+    PPM_ASSIGN(pix, 0, 0, 0);
+    for (i = 0; i < 256 - defaultMap->ncolors; ++i) {
+        ppm_writeppmrow(stdout, &pix, 1, 255, TRUE);
+    }
+
+    return 0;
 }
 
diff --git a/converter/other/pnmtopalm/palmcolor8.map b/converter/other/pnmtopalm/palmcolor8.map
index 2e054616..a4840118 100644
--- a/converter/other/pnmtopalm/palmcolor8.map
+++ b/converter/other/pnmtopalm/palmcolor8.map
@@ -1,235 +1,259 @@
 P3
-232 1
+256 1
 255
-0 0 0
-0 0 0
-0 0 51
-0 0 102
-0 0 153
-0 0 204
+255 255 255
+255 204 255
+255 153 255
+255 102 255
+255 51 255
+255 0 255
+255 255 204
+255 204 204
+255 153 204
+255 102 204
+255 51 204
+255 0 204
+255 255 153
+255 204 153
+255 153 153
+255 102 153
+255 51 153
+255 0 153
+204 255 255
+204 204 255
+204 153 255
+204 102 255
+204 51 255
+204 0 255
+204 255 204
+204 204 204
+204 153 204
+204 102 204
+204 51 204
+204 0 204
+204 255 153
+204 204 153
+204 153 153
+204 102 153
+204 51 153
+204 0 153
+153 255 255
+153 204 255
+153 153 255
+153 102 255
+153 51 255
+153 0 255
+153 255 204
+153 204 204
+153 153 204
+153 102 204
+153 51 204
+153 0 204
+153 255 153
+153 204 153
+153 153 153
+153 102 153
+153 51 153
+153 0 153
+102 255 255
+102 204 255
+102 153 255
+102 102 255
+102 51 255
+102 0 255
+102 255 204
+102 204 204
+102 153 204
+102 102 204
+102 51 204
+102 0 204
+102 255 153
+102 204 153
+102 153 153
+102 102 153
+102 51 153
+102 0 153
+51 255 255
+51 204 255
+51 153 255
+51 102 255
+51 51 255
+51 0 255
+51 255 204
+51 204 204
+51 153 204
+51 102 204
+51 51 204
+51 0 204
+51 255 153
+51 204 153
+51 153 153
+51 102 153
+51 51 153
+51 0 153
+0 255 255
+0 204 255
+0 153 255
+0 102 255
+0 51 255
 0 0 255
-0 51 0
-0 51 51
-0 51 102
-0 51 153
+0 255 204
+0 204 204
+0 153 204
+0 102 204
 0 51 204
-0 51 255
-0 102 0
-0 102 51
-0 102 102
+0 0 204
+0 255 153
+0 204 153
+0 153 153
 0 102 153
-0 102 204
-0 102 255
-0 128 0
-0 128 128
-0 153 0
-0 153 51
+0 51 153
+0 0 153
+255 255 102
+255 204 102
+255 153 102
+255 102 102
+255 51 102
+255 0 102
+255 255 51
+255 204 51
+255 153 51
+255 102 51
+255 51 51
+255 0 51
+255 255 0
+255 204 0
+255 153 0
+255 102 0
+255 51 0
+255 0 0
+204 255 102
+204 204 102
+204 153 102
+204 102 102
+204 51 102
+204 0 102
+204 255 51
+204 204 51
+204 153 51
+204 102 51
+204 51 51
+204 0 51
+204 255 0
+204 204 0
+204 153 0
+204 102 0
+204 51 0
+204 0 0
+153 255 102
+153 204 102
+153 153 102
+153 102 102
+153 51 102
+153 0 102
+153 255 51
+153 204 51
+153 153 51
+153 102 51
+153 51 51
+153 0 51
+153 255 0
+153 204 0
+153 153 0
+153 102 0
+153 51 0
+153 0 0
+102 255 102
+102 204 102
+102 153 102
+102 102 102
+102 51 102
+102 0 102
+102 255 51
+102 204 51
+102 153 51
+102 102 51
+102 51 51
+102 0 51
+102 255 0
+102 204 0
+102 153 0
+102 102 0
+102 51 0
+102 0 0
+51 255 102
+51 204 102
+51 153 102
+51 102 102
+51 51 102
+51 0 102
+51 255 51
+51 204 51
+51 153 51
+51 102 51
+51 51 51
+51 0 51
+51 255 0
+51 204 0
+51 153 0
+51 102 0
+51 51 0
+51 0 0
+0 255 102
+0 204 102
 0 153 102
-0 153 153
-0 153 204
-0 153 255
-0 204 0
+0 102 102
+0 51 102
+0 0 102
+0 255 51
 0 204 51
-0 204 102
-0 204 153
-0 204 204
-0 204 255
+0 153 51
+0 102 51
+0 51 51
+0 0 51
 0 255 0
-0 255 51
-0 255 102
-0 255 153
-0 255 204
-0 255 255
+0 204 0
+0 153 0
+0 102 0
+0 51 0
 17 17 17
 34 34 34
-51 0 0
-51 0 51
-51 0 102
-51 0 153
-51 0 204
-51 0 255
-51 51 0
-51 51 51
-51 51 102
-51 51 153
-51 51 204
-51 51 255
-51 102 0
-51 102 51
-51 102 102
-51 102 153
-51 102 204
-51 102 255
-51 153 0
-51 153 51
-51 153 102
-51 153 153
-51 153 204
-51 153 255
-51 204 0
-51 204 51
-51 204 102
-51 204 153
-51 204 204
-51 204 255
-51 255 0
-51 255 51
-51 255 102
-51 255 153
-51 255 204
-51 255 255
 68 68 68
 85 85 85
-102 0 0
-102 0 51
-102 0 102
-102 0 153
-102 0 204
-102 0 255
-102 51 0
-102 51 51
-102 51 102
-102 51 153
-102 51 204
-102 51 255
-102 102 0
-102 102 51
-102 102 102
-102 102 153
-102 102 204
-102 102 255
-102 153 0
-102 153 51
-102 153 102
-102 153 153
-102 153 204
-102 153 255
-102 204 0
-102 204 51
-102 204 102
-102 204 153
-102 204 204
-102 204 255
-102 255 0
-102 255 51
-102 255 102
-102 255 153
-102 255 204
-102 255 255
 119 119 119
-128 0 0
-128 0 128
 136 136 136
-153 0 0
-153 0 51
-153 0 102
-153 0 153
-153 0 204
-153 0 255
-153 51 0
-153 51 51
-153 51 102
-153 51 153
-153 51 204
-153 51 255
-153 102 0
-153 102 51
-153 102 102
-153 102 153
-153 102 204
-153 102 255
-153 153 0
-153 153 51
-153 153 102
-153 153 153
-153 153 204
-153 153 255
-153 204 0
-153 204 51
-153 204 102
-153 204 153
-153 204 204
-153 204 255
-153 255 0
-153 255 51
-153 255 102
-153 255 153
-153 255 204
-153 255 255
 170 170 170
 187 187 187
-192 192 192
-204 0 0
-204 0 51
-204 0 102
-204 0 153
-204 0 204
-204 0 255
-204 51 0
-204 51 51
-204 51 102
-204 51 153
-204 51 204
-204 51 255
-204 102 0
-204 102 51
-204 102 102
-204 102 153
-204 102 204
-204 102 255
-204 153 0
-204 153 51
-204 153 102
-204 153 153
-204 153 204
-204 153 255
-204 204 0
-204 204 51
-204 204 102
-204 204 153
-204 204 204
-204 204 255
-204 255 0
-204 255 51
-204 255 102
-204 255 153
-204 255 204
-204 255 255
 221 221 221
 238 238 238
-255 0 0
-255 0 51
-255 0 102
-255 0 153
-255 0 204
-255 0 255
-255 51 0
-255 51 51
-255 51 102
-255 51 153
-255 51 204
-255 51 255
-255 102 0
-255 102 51
-255 102 102
-255 102 153
-255 102 204
-255 102 255
-255 153 0
-255 153 51
-255 153 102
-255 153 153
-255 153 204
-255 153 255
-255 204 0
-255 204 51
-255 204 102
-255 204 153
-255 204 204
-255 204 255
-255 255 0
-255 255 51
-255 255 102
-255 255 153
-255 255 204
-255 255 255
+192 192 192
+128 0 0
+128 0 128
+0 128 0
+0 128 128
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
index ee43be7a..88088817 100644
--- a/converter/other/pnmtopalm/palmtopnm.c
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
index d5f79619..90737b78 100644
--- a/converter/other/pnmtopalm/pnmtopalm.c
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -21,6 +21,7 @@
 #include <assert.h>
 #include <limits.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "palm.h"
 #include "shhopt.h"
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
index 23c7bd3a..e16afb14 100644
--- a/converter/other/pnmtopclxl.c
+++ b/converter/other/pnmtopclxl.c
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <ctype.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -153,7 +154,7 @@ parseCommandLine(int argc, char ** argv,
         bool found;
         int i;
         for (i = 0, found=FALSE; xlPaperFormats[i].name && !found; ++i) {
-            if (STREQ(xlPaperFormats[i].name, formatOpt)) {
+            if (streq(xlPaperFormats[i].name, formatOpt)) {
                 found = TRUE;
                 cmdlineP->format = xlPaperFormats[i].xl_nr;
             }
@@ -178,7 +179,7 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->sourceP->name = "-";
         cmdlineP->sourceP->next = NULL;
     } else {
-        int i;
+        unsigned int i;
         InputSource ** nextLinkP;
 
         nextLinkP = &cmdlineP->sourceP;
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 92c38a2a..52f69423 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -52,8 +52,6 @@
    because xels were only 24 bits.  Now they're 96.
 */
    
-#define GRR_GRAY_PALETTE_FIX
-
 #ifndef PNMTOPNG_WARNING_LEVEL
 #  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
 #endif                               /*  2 for warnings (1 == error) */
@@ -62,6 +60,8 @@
 #include <string.h> /* strcat() */
 #include <limits.h>
 #include <png.h>    /* includes zlib.h and setjmp.h */
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "pngtxt.h"
 #include "shhopt.h"
@@ -69,10 +69,12 @@
 #include "nstring.h"
 #include "version.h"
 
+/* A hack until we can remove direct access to png_info from the program */
 #if PNG_LIBPNG_VER >= 10400
-#error Your PNG library (<png.h>) is incompatible with this Netpbm source code.
-#error You need either an older PNG library (older than 1.4)
-#error newer Netpbm source code (at least 10.47.04)
+#define trans_values trans_color
+#define TRANS_ALPHA trans_alpha
+#else
+#define TRANS_ALPHA trans
 #endif
 
 
@@ -390,7 +392,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -471,7 +473,7 @@ parseCommandLine(int argc, char ** argv,
     }
 
     if (cmdlineP->zlibCompression.methodSpec) {
-        if (STREQ(compMethod, "deflated"))
+        if (streq(compMethod, "deflated"))
             cmdlineP->zlibCompression.method = Z_DEFLATED;
         else
             pm_error("The only valid value for -method is 'deflated'.  "
@@ -479,9 +481,9 @@ parseCommandLine(int argc, char ** argv,
     }
 
     if (cmdlineP->zlibCompression.strategySpec) {
-        if (STREQ(compStrategy, "huffman_only"))
+        if (streq(compStrategy, "huffman_only"))
             cmdlineP->zlibCompression.strategy = Z_HUFFMAN_ONLY;
-        else if (STREQ(compStrategy, "filtered"))
+        else if (streq(compStrategy, "filtered"))
             cmdlineP->zlibCompression.strategy = Z_FILTERED;
         else
             pm_error("Valid values for -strategy are 'huffman_only' and "
@@ -1060,7 +1062,7 @@ findRedundantBits(FILE *         const ifp,
 /*----------------------------------------------------------------------------
    Find out if we can use just a subset of the bits from each input
    sample.  Often, people create an image with e.g. 8 bit samples from
-   one that has e.g. only 4 bit samples by scaling by 256/16, which is
+   one that has e.g. only 4 bit samples by scaling by 255/15, which is
    the same as repeating the bits.  E.g.  1011 becomes 10111011.  We
    detect this case.  We return as *meaningfulBitsP the minimum number
    of bits, starting from the least significant end, that contain
@@ -1352,9 +1354,9 @@ computeUnsortedAlphaPalette(FILE *           const ifP,
 
 
 static void
-sortAlphaPalette(gray *         const alphas_of_color[],
-                 unsigned int   const alphas_first_index[],
-                 unsigned int   const alphas_of_color_cnt[],
+sortAlphaPalette(gray *         const alphasOfColor[],
+                 unsigned int   const alphasFirstIndex[],
+                 unsigned int   const alphasOfColorCnt[],
                  unsigned int   const colors,
                  gray           const alphaMaxval,
                  unsigned int         mapping[],
@@ -1370,40 +1372,56 @@ sortAlphaPalette(gray *         const alphas_of_color[],
    palette of the alpha/color pair whose index is x in the unsorted
    PNG palette.  This mapping sorts the palette so that opaque entries
    are last.
+
+   The unsorted PNG palette is sorted enough that all entries for a particular
+   color (with varying transparencies) are contiguous.  alphasFirstIndex[x] is
+   the index in the unsorted PNG palette of the first entry with color x
+   (where x is an index into some other palette).  alphasOfColorCnt[x] is the
+   number of non-opaque entries in the unsorted PNG palette with color x.
+
+   alphasOfColor[x][y] is the y'th alpha value for color x, in no particular
+   order.
+
+   Return as *transSizeP the number of non-opaque entries in the palette
+   (i.e. the index in the palette of the first opaque entry).
 -----------------------------------------------------------------------------*/
-    unsigned int bot_idx;
-    unsigned int top_idx;
-    unsigned int colorIndex;
+    if (colors == 0)
+        *transSizeP = 0;
+    else {
+        unsigned int bot_idx;
+        unsigned int top_idx;
+        unsigned int colorIndex;
     
-    /* We start one index at the bottom of the palette index range
-       and another at the top.  We run through the unsorted palette,
-       and when we see an opaque entry, we map it to the current top
-       cursor and bump it down.  When we see a non-opaque entry, we map 
-       it to the current bottom cursor and bump it up.  Because the input
-       and output palettes are the same size, the two cursors should meet
-       right when we process the last entry of the unsorted palette.
-    */    
-    bot_idx = 0;
-    top_idx = alphas_first_index[colors-1] + alphas_of_color_cnt[colors-1] - 1;
+        /* We start one index at the bottom of the palette index range
+           and another at the top.  We run through the unsorted palette,
+           and when we see an opaque entry, we map it to the current top
+           cursor and bump it down.  When we see a non-opaque entry, we map 
+           it to the current bottom cursor and bump it up.  Because the input
+           and output palettes are the same size, the two cursors should meet
+           right when we process the last entry of the unsorted palette.
+        */    
+        bot_idx = 0;
+        top_idx = alphasFirstIndex[colors-1] + alphasOfColorCnt[colors-1] - 1;
     
-    for (colorIndex = 0;  colorIndex < colors;  ++colorIndex) {
-        unsigned int j;
-        for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) {
-            unsigned int const paletteIndex = 
-                alphas_first_index[colorIndex] + j;
-            if (alphas_of_color[colorIndex][j] == alphaMaxval)
-                mapping[paletteIndex] = top_idx--;
-            else
-                mapping[paletteIndex] = bot_idx++;
+        for (colorIndex = 0;  colorIndex < colors;  ++colorIndex) {
+            unsigned int j;
+            for (j = 0; j < alphasOfColorCnt[colorIndex]; ++j) {
+                unsigned int const paletteIndex = 
+                    alphasFirstIndex[colorIndex] + j;
+                if (alphasOfColor[colorIndex][j] == alphaMaxval)
+                    mapping[paletteIndex] = top_idx--;
+                else
+                    mapping[paletteIndex] = bot_idx++;
+            }
         }
+        /* indices should have just crossed paths */
+        if (bot_idx != top_idx + 1) {
+            pm_error ("internal inconsistency: "
+                      "remapped bot_idx = %u, top_idx = %u",
+                      bot_idx, top_idx);
+        }
+        *transSizeP = bot_idx;
     }
-    /* indices should have just crossed paths */
-    if (bot_idx != top_idx + 1) {
-        pm_error ("internal inconsistency: "
-                  "remapped bot_idx = %u, top_idx = %u",
-                  bot_idx, top_idx);
-    }
-    *transSizeP = bot_idx;
 }
 
 
@@ -1453,7 +1471,7 @@ compute_alpha_palette(FILE *         const ifP,
     getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
            &chv, &colors);
 
-    assert(colors < ARRAY_SIZE(alphas_of_color));
+    assert(colors <= ARRAY_SIZE(alphas_of_color));
 
     computeUnsortedAlphaPalette(ifP, cols, rows, maxval, format, rasterPos,
                                 alpha_mask, chv, colors,
@@ -1817,6 +1835,7 @@ computeColorMap(FILE *         const ifP,
                 int            const cols,
                 int            const rows,
                 xelval         const maxval,
+                int            const pnmType,
                 int            const format,
                 bool           const force,
                 FILE *         const pfP,
@@ -1864,7 +1883,7 @@ computeColorMap(FILE *         const ifP,
                   maxval, PALETTEMAXVAL);
     else {
         unsigned int bitsPerPixel;
-        computePixelWidth(PNM_FORMAT_TYPE(format), pnm_meaningful_bits, alpha,
+        computePixelWidth(pnmType, pnm_meaningful_bits, alpha,
                           NULL, &bitsPerPixel);
 
         if (!pfP && bitsPerPixel == 1)
@@ -2042,7 +2061,7 @@ createPngPalette(pixel              palette_pnm[],
     for (i = 0; i < transSize; ++i) {
         unsigned int const newmv = PALETTEMAXVAL;
         unsigned int const oldmv = alpha_maxval;
-        trans[i] = (trans_pnm[i] * newmv + (oldmv/2)) / oldmv;
+        trans[i] = ROUNDDIV(trans_pnm[i] * newmv, oldmv);
     }
 }
 
@@ -2347,12 +2366,12 @@ convertpnm(struct cmdlineInfo const cmdline,
          of the input image.
       */
   int transexact;  
-    /* boolean: the user wants only the exact color he specified to be
-       transparent; not just something close to it.
-    */
+      /* boolean: the user wants only the exact color he specified to be
+         transparent; not just something close to it.
+      */
   int transparent;
   bool alpha;
-    /* There will be an alpha mask */
+      /* There will be an alpha mask */
   unsigned int pnm_meaningful_bits;
   pixel backcolor;
       /* The background color, with maxval equal to that of the input
@@ -2397,7 +2416,7 @@ convertpnm(struct cmdlineInfo const cmdline,
       */
   unsigned int fulldepth;
       /* The total number of bits per pixel in the (uncompressed) png
-         raster, including all channels 
+         raster, including all channels.
       */
   pm_filepos rasterPos;  
       /* file position in input image file of start of image (i.e. after
@@ -2470,8 +2489,8 @@ convertpnm(struct cmdlineInfo const cmdline,
          to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval
          scaling, and this is more precise.
       */
-      PPM_DEPTH (transcolor, ppm_parsecolor(transstring2, maxmaxval),
-                 maxmaxval, maxval);
+      PPM_DEPTH(transcolor, ppm_parsecolor(transstring2, maxmaxval),
+                maxmaxval, maxval);
   }
   if (cmdline.alpha) {
     pixel alpha_transcolor;
@@ -2550,7 +2569,7 @@ convertpnm(struct cmdlineInfo const cmdline,
   findRedundantBits(ifp, rasterPos, cols, rows, maxval, format, alpha,
                     cmdline.force, &pnm_meaningful_bits);
   
-  computeColorMap(ifp, rasterPos, cols, rows, maxval, format,
+  computeColorMap(ifp, rasterPos, cols, rows, maxval, pnm_type, format,
                   cmdline.force, pfp,
                   alpha, transparent >= 0, transcolor, transexact, 
                   !!cmdline.background, backcolor,
@@ -2630,7 +2649,7 @@ convertpnm(struct cmdlineInfo const cmdline,
     info_ptr->num_palette = palette_size;
     if (trans_size > 0) {
         info_ptr->valid |= PNG_INFO_tRNS;
-        info_ptr->trans = trans;
+        info_ptr->TRANS_ALPHA = trans;
         info_ptr->num_trans = trans_size;   /* omit opaque values */
     }
     /* creating hIST chunk */
@@ -2809,7 +2828,7 @@ main(int argc, char *argv[]) {
 
     int errorlevel;
     
-    pnm_init (&argc, argv);
+    pnm_init(&argc, argv);
     
     parseCommandLine(argc, argv, &cmdline);
     
@@ -2852,3 +2871,6 @@ main(int argc, char *argv[]) {
 
     return errorlevel;
 }
+
+
+
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index fb4d7648..20395952 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -34,6 +34,8 @@
 
 #include <string.h>
 #include <assert.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "mallocvar.h"
 #include "shhopt.h"
@@ -275,6 +277,7 @@ putitem(void) {
         putchar('\n');
         itemsinline = 0;
     }
+    assert(item >> 8 == 0);
     putchar(hexits[item >> 4]);
     putchar(hexits[item & 15]);
     ++itemsinline;
@@ -496,7 +499,7 @@ destroyBmepsOutputEncoder(struct bmepsoe * const bmepsoeP) {
 static void
 outputBmepsSample(struct bmepsoe * const bmepsoeP,
                   unsigned int     const sampleValue,
-          unsigned int     const bitsPerSample) {
+                  unsigned int     const bitsPerSample) {
 
     if (bitsPerSample == 8)
         oe_byte_add(bmepsoeP->oeP, sampleValue);
@@ -954,8 +957,35 @@ putEnd(bool         const showpage,
 
 
 static void
+warnUserAboutReducedDepth(unsigned int const bitsGot,
+                          unsigned int const bitsWanted,
+                          unsigned int const postscriptLevel,
+                          bool         const psFilter) {
+
+    if (bitsGot < bitsWanted) {
+        pm_message("Postscript will have %u bits of color resolution, "
+                   "though the input has %u bits.",
+                   bitsGot, bitsWanted);
+
+        if (postscriptLevel < 2)
+            pm_message("Postscript level %u has a maximum depth of 8 bits.  "
+                       "You could get up to 12 with -level=2 and -psfilter.",
+                       postscriptLevel);
+        else {
+            if (!psFilter)
+                pm_message("You can get up to 12 bits with -psfilter");
+            else
+                pm_message("The Postscript maximum is 12.");
+        }
+    }
+}
+
+
+
+static void
 computeDepth(xelval         const inputMaxval,
              unsigned int   const postscriptLevel, 
+             bool           const psFilter,
              unsigned int * const bitspersampleP,
              unsigned int * const psMaxvalP) {
 /*----------------------------------------------------------------------------
@@ -971,30 +1001,24 @@ computeDepth(xelval         const inputMaxval,
         *bitspersampleP = 2;
     else if (bitsRequiredByMaxval <= 4)
         *bitspersampleP = 4;
-    else        
+    else if (bitsRequiredByMaxval <= 8)
         *bitspersampleP = 8;
-
-    /* There is supposedly a 12 bits per pixel Postscript format, but
-       what?  We produce a raster that is composed of bytes, each
-       coded as a pair of hexadecimal characters and representing 8,
-       4, 2, or 1 pixels.  We also have the RLE format, where some of
-       those bytes are run lengths.
-    */
-
-    if (*bitspersampleP < bitsRequiredByMaxval) {
-        if (bitsRequiredByMaxval <= 12 && postscriptLevel >= 2)
-            pm_message("Maxval of input requires %u bit samples for full "
-                       "resolution, and Postscript level %u is capable "
-                       "of representing that many, but this program "
-                       "doesn't know how.  So we are using %u",
-                       bitsRequiredByMaxval, postscriptLevel, *bitspersampleP);
+    else {
+        /* Post script level 2 defines a format with 12 bits per sample,
+           but I don't know the details of that format (both RLE and
+           non-RLE variations) and existing native raster generation code
+           simply can't handle bps > 8.  But the built-in filters know
+           how to do 12 bps.
+        */
+        if (postscriptLevel >= 2 && psFilter)
+            *bitspersampleP = 12;
         else
-            pm_message("Maxval of input requires %u bit samples for full "
-                       "resolution, but we are using the Postscript level %u "
-                       "maximum of %u",
-                       bitsRequiredByMaxval, postscriptLevel, *bitspersampleP);
+            *bitspersampleP = 8;
     }
 
+    warnUserAboutReducedDepth(*bitspersampleP, bitsRequiredByMaxval,
+                              postscriptLevel, psFilter);
+
     *psMaxvalP = pm_bitstomaxval(*bitspersampleP);
 
     if (verbose)
@@ -1047,8 +1071,7 @@ convertRowPsFilter(struct pam *     const pamP,
     unsigned int const stragglers =
         (((bitsPerSample * pamP->depth) % 8) * pamP->width) % 8;
         /* Number of bits at the right edge that don't fill out a
-           whole byte
-        */
+           whole byte */
 
     unsigned int col;
     tuple scaledTuple;
@@ -1183,7 +1206,8 @@ convertPage(FILE * const ifP,
     if (color)
         pm_message("generating color Postscript program.");
 
-    computeDepth(inpam.maxval, postscriptLevel, &bitspersample, &psMaxval);
+    computeDepth(inpam.maxval, postscriptLevel, psFilter,
+                 &bitspersample, &psMaxval);
     {
         unsigned int const realBitsPerLine = inpam.width * bitspersample;
         unsigned int const paddedBitsPerLine = ((realBitsPerLine + 7) / 8) * 8;
@@ -1275,7 +1299,7 @@ main(int argc, char * argv[]) {
 
     ifp = pm_openr(cmdline.inputFileName);
 
-    if (STREQ(cmdline.inputFileName, "-"))
+    if (streq(cmdline.inputFileName, "-"))
         name = strdup("noname");
     else
         name = basebasename(cmdline.inputFileName);
@@ -1313,7 +1337,7 @@ main(int argc, char * argv[]) {
     strfree(name);
 
     pm_close(ifp);
-    
+
     return 0;
 }
 
diff --git a/converter/other/pnmtorast.c b/converter/other/pnmtorast.c
index 7d1ae05a..605e815c 100644
--- a/converter/other/pnmtorast.c
+++ b/converter/other/pnmtorast.c
@@ -15,15 +15,79 @@
 #include "mallocvar.h"
 
 #define MAXCOLORS 256
-static colormap_t* make_pr_colormap ARGS(( colorhist_vector chv, int colors ));
-static colormap_t* make_gray_pr_colormap ARGS(( void ));
-static colormap_t* alloc_pr_colormap ARGS(( void ));
+
+
+
+static colormap_t *
+alloc_pr_colormap(void) {
+
+    colormap_t* pr_colormapP;
+
+    MALLOCVAR(pr_colormapP);
+    if ( pr_colormapP == NULL )
+        pm_error( "out of memory" );
+    pr_colormapP->type = RMT_EQUAL_RGB;
+    pr_colormapP->length = MAXCOLORS;
+    MALLOCARRAY(pr_colormapP->map[0], MAXCOLORS);
+    MALLOCARRAY(pr_colormapP->map[1], MAXCOLORS);
+    MALLOCARRAY(pr_colormapP->map[2], MAXCOLORS);
+    if ( pr_colormapP->map[0] == NULL || 
+         pr_colormapP->map[1] == NULL ||
+         pr_colormapP->map[2] == NULL )
+        pm_error( "out of memory" );
+
+    return pr_colormapP;
+}
+
+
+
+static colormap_t*
+make_pr_colormap(colorhist_vector const chv,
+                 int              const colors) {
+
+    colormap_t* pr_colormapP;
+    int i;
+
+    pr_colormapP = alloc_pr_colormap( );
+
+    for ( i = 0; i < colors; ++i )
+    {
+        pr_colormapP->map[0][i] = PPM_GETR( chv[i].color );
+        pr_colormapP->map[1][i] = PPM_GETG( chv[i].color );
+        pr_colormapP->map[2][i] = PPM_GETB( chv[i].color );
+    }
+    for ( ; i < MAXCOLORS; ++i )
+        pr_colormapP->map[0][i] = pr_colormapP->map[1][i] =
+            pr_colormapP->map[2][i] = 0;
+
+    return pr_colormapP;
+}
+
+
+
+static colormap_t *
+make_gray_pr_colormap(void) {
+
+    colormap_t* pr_colormapP;
+    int i;
+
+    pr_colormapP = alloc_pr_colormap( );
+
+    for ( i = 0; i < MAXCOLORS; ++i )
+    {
+        pr_colormapP->map[0][i] = i;
+        pr_colormapP->map[1][i] = i;
+        pr_colormapP->map[2][i] = i;
+    }
+
+    return pr_colormapP;
+}
+
+
 
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-{
+main(int argc, char ** argv) {
+
     FILE* ifp;
     xel** xels;
     xel* xelrow;
@@ -247,64 +311,3 @@ main( argc, argv )
     exit( 0 );
 }
 
-static colormap_t*
-make_pr_colormap( chv, colors )
-    colorhist_vector chv;
-    int colors;
-{
-    colormap_t* pr_colormapP;
-    int i;
-
-    pr_colormapP = alloc_pr_colormap( );
-
-    for ( i = 0; i < colors; ++i )
-    {
-        pr_colormapP->map[0][i] = PPM_GETR( chv[i].color );
-        pr_colormapP->map[1][i] = PPM_GETG( chv[i].color );
-        pr_colormapP->map[2][i] = PPM_GETB( chv[i].color );
-    }
-    for ( ; i < MAXCOLORS; ++i )
-        pr_colormapP->map[0][i] = pr_colormapP->map[1][i] =
-            pr_colormapP->map[2][i] = 0;
-
-    return pr_colormapP;
-}
-
-static colormap_t*
-make_gray_pr_colormap( )
-{
-    colormap_t* pr_colormapP;
-    int i;
-
-    pr_colormapP = alloc_pr_colormap( );
-
-    for ( i = 0; i < MAXCOLORS; ++i )
-    {
-        pr_colormapP->map[0][i] = i;
-        pr_colormapP->map[1][i] = i;
-        pr_colormapP->map[2][i] = i;
-    }
-
-    return pr_colormapP;
-}
-
-static colormap_t*
-alloc_pr_colormap( )
-{
-    colormap_t* pr_colormapP;
-
-    MALLOCVAR(pr_colormapP);
-    if ( pr_colormapP == NULL )
-        pm_error( "out of memory" );
-    pr_colormapP->type = RMT_EQUAL_RGB;
-    pr_colormapP->length = MAXCOLORS;
-    MALLOCARRAY(pr_colormapP->map[0], MAXCOLORS);
-    MALLOCARRAY(pr_colormapP->map[1], MAXCOLORS);
-    MALLOCARRAY(pr_colormapP->map[2], MAXCOLORS);
-    if ( pr_colormapP->map[0] == NULL || 
-         pr_colormapP->map[1] == NULL ||
-         pr_colormapP->map[2] == NULL )
-        pm_error( "out of memory" );
-
-    return pr_colormapP;
-}
diff --git a/converter/other/pnmtorle.c b/converter/other/pnmtorle.c
index b7834e89..8908c356 100644
--- a/converter/other/pnmtorle.c
+++ b/converter/other/pnmtorle.c
@@ -59,8 +59,8 @@ static gray    maxval;
  *                                        Read the pnm image file header.
  */
 static void 
-read_pnm_header()
-{
+read_pnm_header(void) {
+
     pnm_readpnminit(fp, &width, &height, &maxval, &format);
     switch (format) {
     case PBM_FORMAT:
@@ -87,12 +87,12 @@ read_pnm_header()
     if (do_alpha)
         VPRINTF(stderr, "Computing alpha channel...\n");
 }
-/*-----------------------------------------------------------------------------
- *                                             Write the rle image file header.
- */
+
+
+
 static void 
-write_rle_header()
-{
+write_rle_header(void) {
+
     hdr.xmin    = 0;
     hdr.xmax    = width-1;
     hdr.ymin    = 0;
@@ -120,101 +120,106 @@ write_rle_header()
     }
     rle_put_setup(&hdr);
 }
-/*-----------------------------------------------------------------------------
- *                                      Write the rle data portion of the file.
- */
+
+
+
 static void 
-write_rle_data()
-{
-    register int     x;
-    register int     scan;
-    register xel     *xelrow, *pP;
-    rle_pixel        ***scanlines, **scanline;
-/*
- * Allocate some memory.
- */
-    /*xelrow = pnm_allowcrow(width);*/
-    xelrow = (xel*) pm_allocrow( width, sizeof(xel) );
+write_rle_data(void) {
+
+    unsigned int scan;
+    xel * xelrow;
+    rle_pixel *** scanlines;
+
+    MALLOCARRAY(xelrow, width);
     MALLOCARRAY(scanlines, height);
-    RLE_CHECK_ALLOC( hdr.cmd, scanlines, "scanline pointers" );
 
-    for ( scan = 0; scan < height; scan++ )
-        RLE_CHECK_ALLOC( hdr.cmd, (rle_row_alloc(&hdr, &scanlines[scan]) >= 0),
-                         "pixel memory" );
-/*
- * Loop through the pnm files image window, read data and flip vertically.
- */
+    RLE_CHECK_ALLOC(hdr.cmd, scanlines, "scanline pointers");
+
+    for (scan = 0; scan < height; ++scan) {
+        int rc;
+        rc = rle_row_alloc(&hdr, &scanlines[scan]);
+        RLE_CHECK_ALLOC(hdr.cmd, rc >= 0, "pixel memory");
+    }
+    /* Loop through the pnm files image window, read data and flip vertically.
+     */
     switch (format) {
     case PBM_FORMAT:
-    case RPBM_FORMAT:
-        for (scan = 0; scan < height; scan++) {
-            scanline = scanlines[height - scan - 1];
+    case RPBM_FORMAT: {
+        unsigned int scan;
+        for (scan = 0; scan < height; ++scan) {
+            rle_pixel ** const scanline = scanlines[height - scan - 1];
+            unsigned int col;
             pnm_readpnmrow(fp, xelrow, width, maxval, format);
-            for (x = 0, pP = xelrow; x < width; x++, pP++) {
-                scanline[RLE_RED][x]   = (PNM_GET1(*pP) ? 255 : 0);
-                if (do_alpha) {
-                    scanline[RLE_ALPHA][x] = scanline[RLE_RED][x];
-                }
+            for (col = 0; col < width; ++col) {
+                scanline[RLE_RED][col] = PNM_GET1(xelrow[col]) ? 255 : 0;
+                if (do_alpha)
+                    scanline[RLE_ALPHA][col] = scanline[RLE_RED][col];
             }
         }
-        break;
+    } break;
     case PGM_FORMAT:
-    case RPGM_FORMAT:
-        for (scan = 0; scan < height; scan++) {
-            scanline = scanlines[height - scan - 1];
+    case RPGM_FORMAT: {
+        unsigned int scan;
+        for (scan = 0; scan < height; ++scan) {
+            rle_pixel ** const scanline = scanlines[height - scan - 1];
+            unsigned int col;
             pnm_readpnmrow(fp, xelrow, width, maxval, format);
-            for (x = 0, pP = xelrow; x < width; x++, pP++) {
-                scanline[RLE_RED][x]   = PNM_GET1(*pP);
-                if (do_alpha) {
-                    scanline[RLE_ALPHA][x] = (scanline[RLE_RED][x] ? 255 : 0);
-                }
+            for (col = 0; col < width; ++col) {
+                scanline[RLE_RED][col] = PNM_GET1(xelrow[col]);
+                if (do_alpha)
+                    scanline[RLE_ALPHA][col] =
+                        scanline[RLE_RED][col] ? 255 : 0;
             }
         }
-        break;
+    } break;
     case PPM_FORMAT:
-    case RPPM_FORMAT:
+    case RPPM_FORMAT: {
+        unsigned int scan;
         for (scan = 0; scan < height; scan++) {
-            scanline = scanlines[height - scan - 1];
+            rle_pixel ** const scanline = scanlines[height - scan - 1];
+            unsigned int col;
             pnm_readpnmrow(fp, xelrow, width, maxval, format);
-            for (x = 0, pP = xelrow; x < width; x++, pP++) {
-                scanline[RLE_RED][x]   = PPM_GETR(*pP);
-                scanline[RLE_GREEN][x] = PPM_GETG(*pP);
-                scanline[RLE_BLUE][x]  = PPM_GETB(*pP);
-                if (do_alpha) {
-                    scanline[RLE_ALPHA][x] = (scanline[RLE_RED][x] ||
-                                              scanline[RLE_GREEN][x] ||
-                                              scanline[RLE_BLUE][x] ? 255 : 0);
-                }
+            for (col = 0; col < width; ++col) {
+                scanline[RLE_RED][col]   = PPM_GETR(xelrow[col]);
+                scanline[RLE_GREEN][col] = PPM_GETG(xelrow[col]);
+                scanline[RLE_BLUE][col]  = PPM_GETB(xelrow[col]);
+                if (do_alpha)
+                    scanline[RLE_ALPHA][col] =
+                        (scanline[RLE_RED][col] ||
+                         scanline[RLE_GREEN][col] ||
+                         scanline[RLE_BLUE][col] ? 255 : 0);
             }
         }
-        break;
+        } break;
     }
-/*
- * Write out data in URT order (bottom to top).
- */
-    for ( scan = 0; scan < height; scan++ ) {
+    /* Write out data in URT order (bottom to top). */
+    for (scan = 0; scan < height; ++scan)
         rle_putrow(scanlines[scan], width, &hdr);
-        rle_row_free( &hdr, scanlines[scan] );
-    }
-    free( scanlines );
+
+    for (scan = 0; scan < height; ++scan)
+        rle_row_free(&hdr, scanlines[scan]);
+    free(scanlines);
+    free(xelrow);
 
     VPRINTF(stderr, "Done -- write eof to RLE data.\n");
     rle_puteof(&hdr);
 }
 
+
+
 int
-main(argc, argv)
-    int argc;
-    char **argv;
-{
-    char     *pnmname = NULL, *outname = NULL;
-    int      oflag, c;
+main(int argc, char **  argv) {
+
+    const char * pnmname;
+    const char * outname;
+    int oflag;
 
     pnm_init(&argc, argv);
 
-/*
- * Get those options.
- */
+    pnmname = NULL;  /* initial value */
+    outname = NULL;  /* initial value */
+
+    /* Get those options. */
     if (!scanargs(argc,argv,
                   "% v%- h%- a%- o%-outfile!s pnmfile%s\n(\
 \tConvert a PNM file to URT RLE format.\n\
@@ -228,37 +233,32 @@ main(argc, argv)
                   &pnmname))
         exit(-1);
 
-    hdr = *rle_hdr_init( (rle_hdr *)NULL );
-    rle_names( &hdr, cmd_name( argv ), outname, 0 );
-/*
- * Open the file.
- */
+    hdr = *rle_hdr_init(NULL);
+    rle_names(&hdr, cmd_name(argv), outname, 0);
+
+    /* Open the file. */
     if (pnmname == NULL) {
-        fp = stdin;
-    }
-    else {
+        fp = pm_openr("-");
+    } else {
         fp = pm_openr(pnmname);
     }
 
     hdr.rle_file = rle_open_f( hdr.cmd, outname, "wb" );
-    while ( (c = getc( fp )) != EOF ) {
-        ungetc( c, fp );
-/*
- * Read the PPM file header.
- */
+
+    if (header)
         read_pnm_header();
-        if (header)
-            break;
-/*
- * Write the rle file header.
- */
-        rle_addhist(argv, (rle_hdr *)NULL, &hdr);
-        write_rle_header();
-/*
- * Write the rle file data.
- */
-        write_rle_data();
+    else {
+        int eof;
+        for (eof = 0; !eof; ) {
+            read_pnm_header();
+            rle_addhist(argv, NULL, &hdr);
+            write_rle_header();
+            write_rle_data();
+            
+            pnm_nextimage(fp, &eof);
+        }
     }
-    fclose(fp);
+    pm_close(fp);
+
     return 0;
 }
diff --git a/converter/other/pnmtotiffcmyk.c b/converter/other/pnmtotiffcmyk.c
index 0fda6b08..2e6ae935 100644
--- a/converter/other/pnmtotiffcmyk.c
+++ b/converter/other/pnmtotiffcmyk.c
@@ -542,10 +542,6 @@ tiffOpen( Out* out, Root *r ) {
   short photometric = PHOTOMETRIC_SEPARATED ; /* ie cmyk */
   int bytesperrow = r->nCols ;
 
-  /* if i don't set stdout non-blocking on my machine then the read
-     that is called inside TIFFFdOpen hangs until the users types ^D.
-     this is also true for pnmtotiff */
-  fcntl( 1, F_SETFL, O_NONBLOCK ) ;
   t->tiff = TIFFFdOpen( 1, "Standard Output", "w" ) ;
   if ( ! t->tiff ) {
     fprintf( stderr, "cannot open tiff stream to standard output\n" ) ;
@@ -794,7 +790,7 @@ standardOpt( Conv *conv, Root *r, int *argn, int argc, char **argv ) {
   }
   /* handle the special case of -1 (no removal) */
   if ( oldn == *argn && pm_keymatch( argv[*argn], "-gammap", 7 ) &&
-       *argn + 1 < argc && STREQ(argv[*argn + 1], "-1") ) {
+       *argn + 1 < argc && streq(argv[*argn + 1], "-1") ) {
     p->remove = 0 ;
     *argn = (*argn) + 2 ;
   } 
diff --git a/converter/other/pnmtoxwd.c b/converter/other/pnmtoxwd.c
index 32fb8a7b..b6439d28 100644
--- a/converter/other/pnmtoxwd.c
+++ b/converter/other/pnmtoxwd.c
@@ -11,6 +11,8 @@
 */
 
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -471,7 +473,7 @@ main(int argc, char * argv[]) {
         }
     }
 
-    if (STREQ(cmdline.inputFilespec, "-"))
+    if (streq(cmdline.inputFilespec, "-"))
         dumpname = "stdin";
     else {
         if (strlen(cmdline.inputFilespec) > XWDVAL_MAX - sizeof(h11) - 1)
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
index a31c3f64..ba1470b0 100644
--- a/converter/other/pstopnm.c
+++ b/converter/other/pstopnm.c
@@ -26,6 +26,7 @@
 #include <sys/wait.h>  
 #include <sys/stat.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -196,8 +197,9 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-add_ps_to_filespec(const char orig_filespec[], char ** const new_filespec_p,
-                   const int verbose) {
+addPsToFilespec(char          const orig_filespec[],
+                const char ** const new_filespec_p,
+                bool          const verbose) {
 /*----------------------------------------------------------------------------
    If orig_filespec[] does not name an existing file, but the same
    name with ".ps" added to the end does, return the name with the .ps
@@ -288,13 +290,13 @@ computeSizeResBlind(unsigned int   const xmax,
 
 
 static void
-compute_size_res(struct cmdlineInfo const cmdline, 
-                 enum orientation   const orientation, 
-                 struct box         const bordered_box,
-                 unsigned int *     const xsizeP, 
-                 unsigned int *     const ysizeP,
-                 unsigned int *     const xresP, 
-                 unsigned int *     const yresP) {
+computeSizeRes(struct cmdlineInfo const cmdline, 
+               enum orientation   const orientation, 
+               struct box         const bordered_box,
+               unsigned int *     const xsizeP, 
+               unsigned int *     const ysizeP,
+               unsigned int *     const xresP, 
+               unsigned int *     const yresP) {
 /*----------------------------------------------------------------------------
   Figure out how big the output image should be (return as
   *xsizeP and *ysizeP) and what output device resolution Ghostscript
@@ -353,7 +355,8 @@ compute_size_res(struct cmdlineInfo const cmdline,
 enum postscript_language {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT};
 
 static enum postscript_language
-language_declaration(const char input_filespec[], int const verbose) {
+languageDeclaration(char const input_filespec[],
+                    bool const verbose) {
 /*----------------------------------------------------------------------------
   Return the Postscript language in which the file declares it is written.
   (Except that if the file is on Standard Input or doesn't validly declare
@@ -361,7 +364,7 @@ language_declaration(const char input_filespec[], int const verbose) {
 -----------------------------------------------------------------------------*/
     enum postscript_language language;
 
-    if (STREQ(input_filespec, "-"))
+    if (streq(input_filespec, "-"))
         /* Can't read stdin, because we need it to remain positioned for the 
            Ghostscript interpreter to read it.
         */
@@ -395,9 +398,9 @@ language_declaration(const char input_filespec[], int const verbose) {
 
 
 static struct box
-compute_box_to_extract(struct box const cmdline_extract_box,
-                       char       const input_filespec[],
-                       bool       const verbose) {
+computeBoxToExtract(struct box const cmdline_extract_box,
+                    char       const input_filespec[],
+                    bool       const verbose) {
 
     struct box retval;
 
@@ -410,7 +413,7 @@ compute_box_to_extract(struct box const cmdline_extract_box,
         */
         struct box ps_bb;  /* Box described by %%BoundingBox stmt in input */
 
-        if (STREQ(input_filespec, "-"))
+        if (streq(input_filespec, "-"))
             /* Can't read stdin, because we need it to remain
                positioned for the Ghostscript interpreter to read it.  
             */
@@ -466,8 +469,8 @@ compute_box_to_extract(struct box const cmdline_extract_box,
 
 
 static enum orientation
-compute_orientation(struct cmdlineInfo const cmdline, 
-                    struct box         const extract_box) {
+computeOrientation(struct cmdlineInfo const cmdline, 
+                   struct box         const extract_box) {
 
     enum orientation retval;
     unsigned int const input_width  = extract_box.urx - extract_box.llx;
@@ -511,9 +514,10 @@ compute_orientation(struct cmdlineInfo const cmdline,
 
 
 static struct box
-add_borders(const struct box input_box, 
-            const float xborder_scale, float yborder_scale,
-            const int verbose) {
+addBorders(struct box const input_box, 
+           float      const xborder_scale,
+           float      const yborder_scale,
+           bool       const verbose) {
 /*----------------------------------------------------------------------------
    Return a box which is 'input_box' plus some borders.
 
@@ -543,9 +547,12 @@ add_borders(const struct box input_box,
 
 
 static const char *
-compute_pstrans(const struct box box, const enum orientation orientation,
-                const int xsize, const int ysize, 
-                const int xres, const int yres) {
+computePstrans(struct box       const box,
+               enum orientation const orientation,
+               int              const xsize,
+               int              const ysize, 
+               int              const xres,
+               int              const yres) {
 
     const char * retval;
 
@@ -570,13 +577,13 @@ compute_pstrans(const struct box box, const enum orientation orientation,
 
 
 static const char *
-compute_outfile_arg(const struct cmdlineInfo cmdline) {
+computeOutfileArg(struct cmdlineInfo const cmdline) {
 
     const char *retval;  /* malloc'ed */
 
     if (cmdline.goto_stdout)
         retval = strdup("-");
-    else if (STREQ(cmdline.input_filespec, "-"))
+    else if (streq(cmdline.input_filespec, "-"))
         retval = strdup("-");
     else {
         char * basename;
@@ -584,7 +591,7 @@ compute_outfile_arg(const struct cmdlineInfo cmdline) {
         
         basename  = strdup(cmdline.input_filespec);
         if (strlen(basename) > 3 && 
-            STREQ(basename+strlen(basename)-3, ".ps")) 
+            streq(basename+strlen(basename)-3, ".ps")) 
             /* The input filespec ends in ".ps".  Chop it off. */
             basename[strlen(basename)-3] = '\0';
 
@@ -605,7 +612,8 @@ compute_outfile_arg(const struct cmdlineInfo cmdline) {
 
 
 static const char *
-compute_gs_device(const int format_type, const int forceplain) {
+computeGsDevice(int  const format_type,
+                bool const forceplain) {
 
     const char * basetype;
     const char * retval;
@@ -673,19 +681,22 @@ findGhostscriptProg(const char ** const retvalP) {
 
 
 static void
-execGhostscript(int const inputPipeFd,
-                const char ghostscript_device[],
-                const char outfile_arg[], 
-                int const xsize, int const ysize, 
-                int const xres, int const yres,
-                const char input_filespec[], int const verbose) {
+execGhostscript(int  const inputPipeFd,
+                char const ghostscript_device[],
+                char const outfile_arg[], 
+                int  const xsize,
+                int  const ysize, 
+                int  const xres,
+                int  const yres,
+                char const input_filespec[],
+                bool const verbose) {
     
-    const char *arg0;
-    const char *ghostscriptProg;
-    const char *deviceopt;
-    const char *outfileopt;
-    const char *gopt;
-    const char *ropt;
+    const char * arg0;
+    const char * ghostscriptProg;
+    const char * deviceopt;
+    const char * outfileopt;
+    const char * gopt;
+    const char * ropt;
     int rc;
 
     findGhostscriptProg(&ghostscriptProg);
@@ -723,15 +734,17 @@ execGhostscript(int const inputPipeFd,
 
 
 
-
 static void
-execute_ghostscript(const char pstrans[], const char ghostscript_device[],
-                    const char outfile_arg[], 
-                    const int xsize, const int ysize, 
-                    const int xres, const int yres,
-                    const char input_filespec[], 
-                    const enum postscript_language language,
-                    const int verbose) {
+executeGhostscript(char                     const pstrans[],
+                   char                     const ghostscript_device[],
+                   char                     const outfile_arg[], 
+                   int                      const xsize,
+                   int                      const ysize, 
+                   int                      const xres,
+                   int                      const yres,
+                   char                     const input_filespec[], 
+                   enum postscript_language const language,
+                   bool                     const verbose) {
 
     int gs_exit;  /* wait4 exit code from Ghostscript */
     FILE *gs;  /* Pipe to Ghostscript's standard input */
@@ -835,10 +848,10 @@ execute_ghostscript(const char pstrans[], const char ghostscript_device[],
 
 
 int
-main(int argc, char **argv) {
+main(int argc, char ** argv) {
 
     struct cmdlineInfo cmdline;
-    char *input_filespec;  /* malloc'ed */
+    const char * input_filespec;  /* malloc'ed */
         /* The file specification of our Postscript input file */
     unsigned int xres, yres;    /* Resolution in pixels per inch */
     unsigned int xsize, ysize;  /* output image size in pixels */
@@ -851,43 +864,42 @@ main(int argc, char **argv) {
 
     enum postscript_language language;
     enum orientation orientation;
-    const char *ghostscript_device;
-    const char *outfile_arg;
-    const char *pstrans;
+    const char * ghostscript_device;
+    const char * outfile_arg;
+    const char * pstrans;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    add_ps_to_filespec(cmdline.input_filespec, &input_filespec,
-                       cmdline.verbose);
+    addPsToFilespec(cmdline.input_filespec, &input_filespec, cmdline.verbose);
 
-    extract_box = compute_box_to_extract(cmdline.extract_box, input_filespec, 
-                                         cmdline.verbose);
+    extract_box = computeBoxToExtract(cmdline.extract_box, input_filespec, 
+                                      cmdline.verbose);
 
-    language = language_declaration(input_filespec, cmdline.verbose);
+    language = languageDeclaration(input_filespec, cmdline.verbose);
     
-    orientation = compute_orientation(cmdline, extract_box);
+    orientation = computeOrientation(cmdline, extract_box);
 
-    bordered_box = add_borders(extract_box, cmdline.xborder, cmdline.yborder,
-                               cmdline.verbose);
+    bordered_box = addBorders(extract_box, cmdline.xborder, cmdline.yborder,
+                              cmdline.verbose);
 
-    compute_size_res(cmdline, orientation, bordered_box, 
-                     &xsize, &ysize, &xres, &yres);
+    computeSizeRes(cmdline, orientation, bordered_box, 
+                   &xsize, &ysize, &xres, &yres);
     
-    pstrans = compute_pstrans(bordered_box, orientation,
-                              xsize, ysize, xres, yres);
+    pstrans = computePstrans(bordered_box, orientation,
+                             xsize, ysize, xres, yres);
 
-    outfile_arg = compute_outfile_arg(cmdline);
+    outfile_arg = computeOutfileArg(cmdline);
 
     ghostscript_device = 
-        compute_gs_device(cmdline.format_type, cmdline.forceplain);
+        computeGsDevice(cmdline.format_type, cmdline.forceplain);
     
     pm_message("Writing %s file", ghostscript_device);
     
-    execute_ghostscript(pstrans, ghostscript_device, outfile_arg, 
-                        xsize, ysize, xres, yres, input_filespec,
-                        language, cmdline.verbose);
+    executeGhostscript(pstrans, ghostscript_device, outfile_arg, 
+                       xsize, ysize, xres, yres, input_filespec,
+                       language, cmdline.verbose);
 
     strfree(ghostscript_device);
     strfree(outfile_arg);
diff --git a/converter/other/rast.h b/converter/other/rast.h
index 1854e495..e79896ea 100644
--- a/converter/other/rast.h
+++ b/converter/other/rast.h
@@ -97,15 +97,33 @@ typedef struct {
 
 /* And the routine definitions. */
 
-struct pixrect* mem_create ARGS(( int w, int h, int depth ));
-void mem_free ARGS(( struct pixrect* p ));
+struct pixrect *
+mem_create(int const w,
+           int const h,
+           int const depth);
 
-int pr_dump ARGS(( struct pixrect* p, FILE* out, colormap_t* colormap, int type, int copy_flag ));
+void
+mem_free(struct pixrect * const p);
 
-int pr_load_header ARGS(( FILE* in, struct rasterfile* hP ));
+int
+pr_dump(struct pixrect * const p,
+        FILE *           const out,
+        colormap_t *     const colormap,
+        int              const type,
+        int              const copy_flag);
 
-int pr_load_colormap ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap ));
+int
+pr_load_header(FILE *              const in,
+               struct rasterfile * const hP);
 
-struct pixrect* pr_load_image ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap ));
+int
+pr_load_colormap(FILE *              const in,
+                 struct rasterfile * const hP,
+                 colormap_t *        const colormap);
+
+struct pixrect *
+pr_load_image(FILE *              const in,
+              struct rasterfile * const hP,
+              colormap_t *        const colormap);
 
 #endif
diff --git a/converter/other/rlatopam.c b/converter/other/rlatopam.c
index ae1326ca..703c4820 100644
--- a/converter/other/rlatopam.c
+++ b/converter/other/rlatopam.c
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <errno.h>
 
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "pam.h"
diff --git a/converter/other/rletopnm.c b/converter/other/rletopnm.c
index aaa86388..83ada51b 100644
--- a/converter/other/rletopnm.c
+++ b/converter/other/rletopnm.c
@@ -46,6 +46,7 @@
 #define NO_DECLARE_MALLOC
 #include <rle.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -124,7 +125,7 @@ parseCommandLine(int argc, char ** argv,
     if (argc - 1 == 0)
         cmdlineP->input_filename = NULL;  /* he wants stdin */
     else if (argc - 1 == 1) {
-        if (STREQ(argv[1], "-"))
+        if (streq(argv[1], "-"))
             cmdlineP->input_filename = NULL;  /* he wants stdin */
         else 
             cmdlineP->input_filename = strdup(argv[1]);
@@ -133,7 +134,7 @@ parseCommandLine(int argc, char ** argv,
                  "is the input file specification");
 
     if (cmdlineP->alpha_filename && 
-        STREQ(cmdlineP->alpha_filename, "-"))
+        streq(cmdlineP->alpha_filename, "-"))
         cmdlineP->alpha_stdout = TRUE;
     else 
         cmdlineP->alpha_stdout = FALSE;
diff --git a/converter/other/svgtopam.c b/converter/other/svgtopam.c
index 76e122db..c7eac8e6 100644
--- a/converter/other/svgtopam.c
+++ b/converter/other/svgtopam.c
@@ -13,6 +13,17 @@
   By Bryan Henderson, San Jose, California.  May 2006
 
   Contributed to the public domain.
+
+==============================================================================
+
+  Implementation notes:
+
+   A look at Libxml2 Subversion source code change history says the type
+   'xmlReaderTypes' was added (in <libxml/xmlreader.h>) in 2.5.9.  But a MacOS
+   10.3.9 user reports in April 2007 that he has 2.6.16 installed and it
+   doesn't have xmlReaderTypes.  Another MacOS user reported that in December
+   2008.  Apparently that OS has a broken libxml2 installation.
+   
 ============================================================================*/
 
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
@@ -205,7 +216,16 @@ makePoint(unsigned int const x,
     return p;
 }
 
+static ppmd_point
+makePpmdPoint(point const arg) {
+
+    ppmd_point p;
 
+    p.x = arg.x;
+    p.y = arg.y;
+
+    return p;
+}
 
 typedef enum {
     PATH_MOVETO,
@@ -437,12 +457,12 @@ outlineObject(path *           const pathP,
                     pm_message("Doing cubic spline to (%u, %u)",
                                dest.x, dest.y);
                 /* We need to write ppmd_spline4() */
-                ppmd_spline4(NULL, 0, 0, 0,
-                             currentPos.x, currentPos.y,
-                             ctl1.x, ctl1.y,
-                             ctl2.x, ctl2.y,
-                             dest.x, dest.y,
-                             ppmd_fill_drawproc, fillObjP);
+                ppmd_spline4p(NULL, 0, 0, 0,
+                              makePpmdPoint(currentPos),
+                              makePpmdPoint(dest),
+                              makePpmdPoint(ctl1),
+                              makePpmdPoint(ctl2),
+                              ppmd_fill_drawprocp, fillObjP);
                 currentPos = dest;
             } break;
             }
@@ -562,6 +582,8 @@ static void
 processSubPathNode(xmlTextReaderPtr const xmlReaderP,
                    bool *           const endOfPathP) {
 
+    /* See comment above about xmlReaderTypes not being defined */
+
     xmlReaderTypes const nodeType  = xmlTextReaderNodeType(xmlReaderP);
 
     *endOfPathP = FALSE;  /* initial assumption */
diff --git a/converter/other/tiff.c b/converter/other/tiff.c
index a498571b..90d50710 100644
--- a/converter/other/tiff.c
+++ b/converter/other/tiff.c
@@ -6,6 +6,8 @@
 
 ============================================================================*/
 
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
+
 #include <string.h>
 
 #ifdef VMS
@@ -40,7 +42,7 @@ number(const char * const value,
         /* It's not a numeric string, so it must be an enumerated value name */
         unsigned int i;
         for (i = 0; tagvallist[i].name; ++i) {
-            if (STRCASEEQ(value, tagvallist[i].name))
+            if (strcaseeq(value, tagvallist[i].name))
                 return tagvallist[i].value;
         }
         pm_error("'%s' is neither a number nor a valid value name", value);
@@ -460,7 +462,7 @@ tagDefFind(const char * const name) {
     for (i = 0;
          i < ARRAY_SIZE(tagDefinitions) && tagDefinitions[i].name;
          ++i) {
-        if (STRCASEEQ(tagDefinitions[i].name, name))
+        if (strcaseeq(tagDefinitions[i].name, name))
             return &tagDefinitions[i];
     }
 
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
index 5969a49a..6665c7fd 100644
--- a/converter/other/tifftopnm.c
+++ b/converter/other/tifftopnm.c
@@ -49,11 +49,16 @@
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
+#include <assert.h>
 #include <string.h>
-#include "pnm.h"
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "pnm.h"
 
 #ifdef VMS
 #ifdef SYSV
@@ -90,13 +95,14 @@ struct cmdlineInfo {
     bool alphaStdout;
     unsigned int respectfillorder;   /* -respectfillorder option */
     unsigned int byrow;
+    unsigned int orientraw;
     unsigned int verbose;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
@@ -122,12 +128,14 @@ parseCommandLine(int argc, char ** argv,
             OPT_FLAG,   NULL, &cmdlineP->respectfillorder,     0);
     OPTENT3(0,   "byrow",   
             OPT_FLAG,   NULL, &cmdlineP->byrow,                0);
+    OPTENT3(0,   "orientraw",   
+            OPT_FLAG,   NULL, &cmdlineP->orientraw,            0);
     OPTENT3('h', "headerdump", 
             OPT_FLAG,   NULL, &cmdlineP->headerdump,           0);
     OPTENT3(0,   "alphaout",   
             OPT_STRING, &cmdlineP->alphaFilename, &alphaSpec,  0);
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (argc - 1 == 0)
         cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
@@ -138,7 +146,7 @@ parseCommandLine(int argc, char ** argv,
                  "is the input file name");
 
     if (alphaSpec) {
-        if (STREQ(cmdlineP->alphaFilename, "-"))
+        if (streq(cmdlineP->alphaFilename, "-"))
             cmdlineP->alphaStdout = TRUE;
         else
             cmdlineP->alphaStdout = FALSE;
@@ -150,16 +158,109 @@ parseCommandLine(int argc, char ** argv,
 
 
 
+static void
+getBps(TIFF *           const tif,
+       unsigned short * const bpsP) {
+
+    unsigned short tiffBps;
+    unsigned short bps;
+    int rc;
+
+    rc = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiffBps);
+    bps = (rc == 0) ? 1 : tiffBps;
+
+    if (bps < 1 || (bps > 8 && bps != 16 && bps != 32))
+        pm_error("This program can process Tiff images with only "
+                 "1-8 or 16 bits per sample.  The input Tiff image "
+                 "has %hu bits per sample.", bps);
+    else
+        *bpsP = bps;
+}
+
+
+
+struct tiffDirInfo {
+    /* 'width' and 'height' are the dimensions of the raster matrix in
+       the TIFF stream -- what the TIFF spec calls the image.  The
+       dimensions of the actual visual image represented may be the
+       reverse, because the raster can represent the visual image in
+       various orientations, as described by 'orientation'.
+    */
+    unsigned int   width;
+    unsigned int   height;
+    unsigned short bps;
+    unsigned short spp;
+    unsigned short photomet;
+    unsigned short planarconfig;
+    unsigned short fillorder;
+    unsigned short orientation;
+};
+
+
+
+static void
+tiffToImageDim(unsigned int   const tiffCols,
+               unsigned int   const tiffRows,
+               unsigned short const orientation,
+               unsigned int * const imageColsP,
+               unsigned int * const imageRowsP) {
+
+    switch (orientation) {
+    case ORIENTATION_TOPLEFT:
+    case ORIENTATION_TOPRIGHT:
+    case ORIENTATION_BOTRIGHT:
+    case ORIENTATION_BOTLEFT:
+        *imageColsP = tiffCols;
+        *imageRowsP = tiffRows;
+        break;
+    case ORIENTATION_LEFTTOP:
+    case ORIENTATION_RIGHTTOP:
+    case ORIENTATION_RIGHTBOT:
+    case ORIENTATION_LEFTBOT:
+        *imageColsP = tiffRows;
+        *imageRowsP = tiffCols;
+        break;
+    default:
+        pm_error("Invalid value for orientation tag in TIFF directory: %u",
+                 orientation);
+    }
+}
+
+static void
+getTiffDimensions(TIFF *         const tiffP,
+                  unsigned int * const colsP,
+                  unsigned int * const rowsP) {
+/*----------------------------------------------------------------------------
+   Return the dimensions of the image represented by *tiffP.  Not the
+   dimensions of the internal raster matrix -- the dimensions of the
+   actual visual image.
+-----------------------------------------------------------------------------*/
+    int ok;
+
+    unsigned int width, length;
+    unsigned short tiffOrientation;
+    unsigned short orientation;
+    int present;
+
+    ok = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &width);
+    if (!ok)
+        pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
+    ok = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &length);
+    if (!ok)
+        pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
+
+    present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+    orientation = present ? tiffOrientation : ORIENTATION_TOPLEFT;
+
+    tiffToImageDim(width, length, orientation, colsP, rowsP);
+}
+
+
+
 static void 
-read_directory(TIFF * const tif,
-               unsigned short * const bps_p,
-               unsigned short * const spp_p,
-               unsigned short * const photomet_p,
-               unsigned short * const planarconfig_p,
-               unsigned short * const fillorder_p,
-               unsigned int *   const cols_p,
-               unsigned int *   const rows_p,
-               bool             const headerdump) {
+readDirectory(TIFF *               const tiffP,
+              bool                 const headerdump,
+              struct tiffDirInfo * const headerP) {
 /*----------------------------------------------------------------------------
    Read various values of TIFF tags from the TIFF directory, and
    default them if not in there and make guesses where values are
@@ -173,68 +274,71 @@ read_directory(TIFF * const tif,
    invalid values to our caller.
 -----------------------------------------------------------------------------*/
     int rc;
-    unsigned short tiff_bps;
-    unsigned short tiff_spp;
+    unsigned short tiffSpp;
 
     if (headerdump)
-        TIFFPrintDirectory(tif, stderr, TIFFPRINT_NONE);
+        TIFFPrintDirectory(tiffP, stderr, TIFFPRINT_NONE);
 
-    rc = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiff_bps);
-    *bps_p = (rc == 0) ? 1 : tiff_bps;
+    getBps(tiffP, &headerP->bps);
 
-    if (*bps_p < 1 || (*bps_p > 8 && *bps_p != 16 && *bps_p != 32))
-        pm_error("This program can process Tiff images with only "
-                 "1-8 or 16 bits per sample.  The input Tiff image "
-                 "has %d bits per sample.", *bps_p);
+    rc = TIFFGetFieldDefaulted(tiffP, TIFFTAG_FILLORDER, &headerP->fillorder);
+    rc = TIFFGetField(tiffP, TIFFTAG_SAMPLESPERPIXEL, &tiffSpp);
+    headerP->spp = (rc == 0) ? 1 : tiffSpp;
 
-    rc = TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, fillorder_p);
-    rc = TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &tiff_spp);
-    *spp_p = (rc == 0) ? 1: tiff_spp;
-
-    rc = TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, photomet_p);
+    rc = TIFFGetField(tiffP, TIFFTAG_PHOTOMETRIC, &headerP->photomet);
     if (rc == 0)
         pm_error("PHOTOMETRIC tag is not in Tiff file.  "
                  "TIFFGetField() of it failed.\n"
                  "This means the input is not valid Tiff.");
 
-    if (*spp_p > 1) {
-        rc = TIFFGetField(tif, TIFFTAG_PLANARCONFIG, planarconfig_p);
+    if (headerP->spp > 1) {
+        rc = TIFFGetField(tiffP, TIFFTAG_PLANARCONFIG, &headerP->planarconfig);
         if (rc == 0)
             pm_error("PLANARCONFIG tag is not in Tiff file, though it "
                      "has more than one sample per pixel.  "
                      "TIFFGetField() of it failed.  This means the input "
                      "is not valid Tiff.");
-    } else {
-        *planarconfig_p = PLANARCONFIG_CONTIG;
-    }
+    } else
+        headerP->planarconfig = PLANARCONFIG_CONTIG;
 
-    switch(*planarconfig_p) {
+    switch (headerP->planarconfig) {
     case PLANARCONFIG_CONTIG:
         break;
     case PLANARCONFIG_SEPARATE:
-        if (*photomet_p != PHOTOMETRIC_RGB && 
-            *photomet_p != PHOTOMETRIC_SEPARATED)
+        if (headerP->photomet != PHOTOMETRIC_RGB && 
+            headerP->photomet != PHOTOMETRIC_SEPARATED)
             pm_error("This program can handle separate planes only "
-                     "with RGB (PHOTOMETRIC tag = %d) or SEPARATED "
-                     "(PHOTOMETRIC tag = %d) data.  The input Tiff file " 
-                     "has PHOTOMETRIC tag = %d.",
-                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED, *photomet_p);
+                     "with RGB (PHOTOMETRIC tag = %u) or SEPARATED "
+                     "(PHOTOMETRIC tag = %u) data.  The input Tiff file " 
+                     "has PHOTOMETRIC tag = %hu.",
+                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED,
+                     headerP->photomet);
         break;
     default:
-        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %d.\n",
-                 *planarconfig_p);
+        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %u.\n",
+                 headerP->planarconfig);
     }
 
-    rc = TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, cols_p);
+    rc = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &headerP->width);
     if (rc == 0)
         pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
-    rc = TIFFGetField( tif, TIFFTAG_IMAGELENGTH, rows_p );
+    rc = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &headerP->height);
     if (rc == 0)
         pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
 
+    {
+        unsigned short tiffOrientation;
+        int present;
+        present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+        headerP->orientation =
+            present ? tiffOrientation : ORIENTATION_TOPLEFT;
+    }
     if (headerdump) {
-        pm_message( "%dx%dx%d image", *cols_p, *rows_p, *bps_p * *spp_p );
-        pm_message( "%d bits/sample, %d samples/pixel", *bps_p, *spp_p );
+        pm_message("%ux%ux%u raster matrix, oriented %u",
+                   headerP->width, headerP->height,
+                   headerP->bps * headerP->spp, headerP->orientation);
+        pm_message("%hu bits/sample, %hu samples/pixel",
+                   headerP->bps, headerP->spp);
     }
 }
 
@@ -260,17 +364,18 @@ readscanline(TIFF *         const tif,
    a Tiff scanline.
 -----------------------------------------------------------------------------*/
     int rc;
-    const unsigned int bpsmask = (1 << bps) - 1;
+    unsigned int const bpsmask = (1 << bps) - 1;
       /* A mask for taking the lowest 'bps' bits of a number */
 
     /* The TIFFReadScanline man page doesn't tell the format of its
        'buf' return value, but it is exactly the same format as the 'buf'
        input to TIFFWriteScanline.  The man page for that doesn't say 
-       anything either, but the source code for Pnmtotiff contains a
+       anything either, but the source code for Pamtotiff contains a
        specification.
     */
 
     rc = TIFFReadScanline(tif, scanbuf, row, plane);
+
     if (rc < 0)
         pm_error( "Unable to read row %d, plane %d of input Tiff image.  "
                   "TIFFReadScanline() failed.",
@@ -291,7 +396,7 @@ readscanline(TIFF *         const tif,
 
         for (sample = 0, bitsleft=8, inP=scanbuf; 
              sample < cols*spp; 
-             sample++) {
+             ++sample) {
             if (bitsleft == 0) {
                 ++inP; 
                 bitsleft = 8;
@@ -322,7 +427,7 @@ readscanline(TIFF *         const tif,
            bytes of each sample appear in a TIFF file, which is
            contrary to the TIFF spec.  
         */
-        uint16 * const scanbuf16 = (uint16 *) scanbuf;
+        const uint16 * const scanbuf16 = (const uint16 *) scanbuf;
         unsigned int sample;
 
         for (sample = 0; sample < cols*spp; ++sample)
@@ -547,6 +652,442 @@ analyzeImageType(TIFF *             const tif,
 
 
 
+typedef struct {
+    FILE *       imageoutFileP;
+        /* The stream to which we write the PNM image.  Null for none. */
+    FILE *       alphaFileP;
+        /* The stream to which we write the alpha channel.  Null for none. */
+    unsigned int inCols;
+        /* Width of each row that gets passed to this object */
+    unsigned int inRows;
+        /* Number of rows that get passed to this object */
+    unsigned int outCols;
+        /* Width of each row this object writes out to the file */
+    unsigned int outRows;
+        /* Number of rows this object writes out */
+    xelval       maxval;
+        /* Maxval of the output image */
+    int          format;
+        /* Format of the output image */
+    gray         alphaMaxval;
+        /* Maxval of the alpha channel */
+    bool         flipping;
+        /* We're passing rows through a Pamflip process, rather than writing
+           them directly to *imageoutFileP, *alphaFileP.
+        */
+    FILE *       imagePipeP;
+        /* Stream hooked up to pipe that goes to a Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+    FILE *       alphaPipeP;
+        /* Stream hooked up to pipe that goes to a Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+    pid_t        imageFlipPid;
+        /* Process ID of the Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+    pid_t        alphaFlipPid;
+        /* Process ID of the Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+} pnmOut;
+
+
+
+static const char *
+xformNeeded(unsigned short const tiffOrientation) {
+/*----------------------------------------------------------------------------
+   Return the value of the Pamflip -xform option that causes Pamflip
+   to change a raster from orienation 'tiffOrientation' to Row 0 top,
+   Column 0 left.
+-----------------------------------------------------------------------------*/
+    switch (tiffOrientation) {
+    case ORIENTATION_TOPLEFT:  return "";
+    case ORIENTATION_TOPRIGHT: return "leftright";
+    case ORIENTATION_BOTRIGHT: return "topbottom,leftright";
+    case ORIENTATION_BOTLEFT:  return "topbottom";
+    case ORIENTATION_LEFTTOP:  return "transpose";
+    case ORIENTATION_RIGHTTOP: return "transpose,leftright";
+    case ORIENTATION_RIGHTBOT: return "transpose,topbottom,leftright";
+    case ORIENTATION_LEFTBOT:  return "transpose,topbottom";
+    default:
+        pm_error("Invalid value for orientation tag in TIFF directory: %u",
+                 tiffOrientation);
+        return "";
+    }
+}
+
+
+
+/* File descriptors array indices for use with pipe() */
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+static void
+spawnWithInputPipe(const char *  const shellCmd,
+                   FILE **       const pipePP,
+                   pid_t *       const pidP,
+                   const char ** const errorP) {
+
+    int fd[2];
+    int rc;
+
+    rc = pipe(fd);
+
+    if (rc != 0)
+        asprintfN(errorP, "Failed to create pipe for process input.  "
+                  "Errno=%d (%s)", errno, strerror(errno));
+    else {
+        int rc;
+
+        rc = fork();
+
+        if (rc < 0) {
+            asprintfN(errorP, "Failed to fork a process.  errno=%d (%s)",
+                      errno, strerror(errno));
+        } else if (rc == 0) {
+            /* This is the child */
+            int rc;
+            close(fd[PIPE_WRITE]);
+            close(STDIN_FILENO);
+            dup2(fd[PIPE_READ], STDIN_FILENO);
+
+            rc = system(shellCmd);
+
+            exit(rc);
+        } else {
+            /* Parent */
+            pid_t const childPid = rc;
+
+            close(fd[PIPE_READ]);
+
+            *pidP   = childPid;
+            *pipePP = fdopen(fd[PIPE_WRITE], "w");
+
+            if (*pipePP == NULL)
+                asprintfN(errorP,"Unable to create stream from pipe.  "
+                          "fdopen() fails with errno=%d (%s)",
+                          errno, strerror(errno));
+            else
+                *errorP = NULL;
+        }
+    }
+}
+
+                  
+
+static void
+createFlipProcess(FILE *         const outFileP,
+                  unsigned short const orientation,
+                  bool           const verbose,
+                  FILE **        const inPipePP,
+                  pid_t *        const pidP) {
+/*----------------------------------------------------------------------------
+   Create a process that runs the program Pamflip and writes its output
+   to *imageoutFileP.
+
+   The process takes it input from a pipe that we create.  We return as
+   *inPipePP a file stream connected to the other end of that pipe.
+
+   I.e. Caller will write a Netpbm file stream to **inPipePP and a flipped
+   version of it will go to *outFileP.
+
+   The flipping it does turns the input from orientation 'orientation'
+   to Netpbm orientation, i.e. raster row 0 top, raster column 0 left,
+   where the raster stream is divided into rows, with row 0 being first
+   in the stream, and column 0 being first within each row.
+
+   Caller must close *inPipePP when he is done.
+-----------------------------------------------------------------------------*/
+    const char * pamflipCmd;
+    const char * error;
+
+    /* Hooking up the process to the output stream is kind of tricky
+       because the stream (FILE *) is an entity local to this process.
+       We just assume that nothing in this process actually touches
+       the stream so that having the process write to the underlying
+       file descriptor is equivalent to writing to the stream.
+    */
+
+    asprintfN(&pamflipCmd, "pamflip -xform=%s >&%u",
+              xformNeeded(orientation), fileno(outFileP));
+
+    if (verbose)
+        pm_message("Reorienting raster with shell command '%s'", pamflipCmd);
+
+    spawnWithInputPipe(pamflipCmd, inPipePP, pidP, &error);
+
+    if (error) {
+        pm_error("Shell command '%s', to reorient the TIFF "
+                 "raster, failed.  %s.  To work around this, you can use "
+                 "the -orientraw option.", pamflipCmd, error);
+
+        strfree(error);
+    }
+}
+
+
+
+static void
+setupFlipper(pnmOut *       const pnmOutP,
+             unsigned short const orientation,
+             bool           const flipIfNeeded,
+             bool           const orientraw,
+             bool           const verbose,
+             bool *         const flipOkP,
+             bool *         const noflipOkP) {
+/*----------------------------------------------------------------------------
+   Set up the Pamflip processes to flip the raster, where needed.
+
+   Whether we need a Pamflip process is a complex decision.  For
+   reasons of efficiency and robustness, we don't want one unless
+   flipping is actually required.  Flipping is not required if the
+   TIFF image is already oriented like a PNM.  It also isn't required
+   if the user explicitly asks for raw orientation.  Finally, it's not
+   required when the user is using the TIFF library whole-image
+   conversion services, because they do the flipping and thus the
+   'pnmOut' object will see properly oriented pixels as input.
+
+   'flipIfNeeded' says to set up the flipping pipe if 'orientation'
+   indicates something other than standard PNM orientation.
+
+   'orientation' is the orientation of the TIFF raster.
+
+   'orientraw' says the final output should be in the same
+   orientation, 'orientation', not the proper PNM orientation.
+
+   *flipOkP means that as we set up the pnmOut object,
+   it is OK if Caller flips the raster on his own.  *noflipOk means
+   is is OK if Caller does not flip the raster on his own.  Note
+   that they are both true if the raster is already in the PNM
+   orientation, because then flipping is idempotent.
+-----------------------------------------------------------------------------*/
+
+    if (orientation == ORIENTATION_TOPLEFT) {
+        /* Ah, the easy case.  Flipping and not flipping are identical,
+           so none of the other parameters matter.  Just write directly
+           to the output file and let Caller flip or not flip as he
+           wishes.
+        */
+        pnmOutP->flipping = FALSE;
+        *flipOkP   = TRUE;
+        *noflipOkP = TRUE;
+    } else {
+        if (orientraw) {
+            /* Raster is not to be flipped, so go directly to file,
+               and tell Caller not to flip it either.
+            */
+            pnmOutP->flipping = FALSE;
+            *flipOkP   = FALSE;
+            *noflipOkP = TRUE;
+        } else {            
+            if (flipIfNeeded) {
+                if (verbose)
+                    pm_message("Transforming raster with Pamflip");
+
+                if (pnmOutP->alphaFileP)
+                    createFlipProcess(pnmOutP->alphaFileP, orientation,
+                                      verbose,
+                                      &pnmOutP->alphaPipeP,
+                                      &pnmOutP->alphaFlipPid);
+                if (pnmOutP->imageoutFileP)
+                    createFlipProcess(pnmOutP->imageoutFileP, orientation,
+                                      verbose,
+                                      &pnmOutP->imagePipeP,
+                                      &pnmOutP->imageFlipPid);
+                
+                /* The stream will flip it, so Caller must not: */
+                pnmOutP->flipping = TRUE;
+                *flipOkP   = FALSE;
+                *noflipOkP = TRUE;
+            } else {
+                /* It needs flipping, but Caller doesn't want us to do it.
+                   So Caller must do it:
+                */
+                pnmOutP->flipping = FALSE;
+                *flipOkP   = TRUE;
+                *noflipOkP = FALSE;
+            }
+        }
+    }
+}
+
+
+
+static void
+computeOutputDimensions(unsigned int       const tiffCols,
+                        unsigned int       const tiffRows,
+                        unsigned short     const orientation,
+                        bool               const orientraw,
+                        unsigned int *     const colsP,
+                        unsigned int *     const rowsP,
+                        bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Compute the dimensions of the image.  We're talking about the
+   actual image, not what the TIFF spec calls the image.  What the
+   TIFF spec calls the image is matrix of pixels within the TIFF file.
+   That matrix can represent the actual image in various ways.
+   E.g. the first row of the matrix might be the right edge of the
+   image.
+
+   'tiffCols' and 'tiffRows' are the images of the TIFF matrix and
+   'orientation' is the orientation of that matrix.
+
+   'orientraw' says we want to output the matrix in its natural
+   orientation, not the true image.
+-----------------------------------------------------------------------------*/
+    if (orientraw) {
+        *colsP = tiffCols;
+        *rowsP = tiffRows;
+    } else
+        tiffToImageDim(tiffCols, tiffRows, orientation, colsP, rowsP);
+
+    if (verbose)
+        pm_message("Generating %uw x %uh PNM image", *colsP, *rowsP);
+}
+
+
+
+static void
+pnmOut_init(FILE *         const imageoutFileP,
+            FILE *         const alphaFileP,
+            unsigned int   const cols,
+            unsigned int   const rows,
+            unsigned short const orientation,
+            xelval         const maxval,
+            int            const format,
+            gray           const alphaMaxval,
+            bool           const flipIfNeeded,
+            bool           const orientraw,
+            bool           const verbose,
+            bool *         const flipOkP,
+            bool *         const noflipOkP,
+            pnmOut *       const pnmOutP) {
+/*----------------------------------------------------------------------------
+   'cols' and 'rows' are the dimensions of the raster matrix which is
+   oriented according to 'orientation' with respect to the image.
+
+   If the user flips the data before giving it to the pnmOut object,
+   pnmOut may see the inverse dimensions; if the pnmOut object flips the
+   data, pnmOut get 'cols' x 'rows' data, but its output file may be
+   'rows x cols'.
+
+   Because the pnmOut object must be set up to receive flipped or not
+   flipped input, we have *flipOkP and *noflipOkP outputs that tell
+   Caller whether he has to flip or not.  In the unique case that
+   the TIFF matrix is already oriented the way the output PNM file needs
+   to be, flipping is idempotent, so both *flipOkP and *noflipOkP are
+   true.
+-----------------------------------------------------------------------------*/
+    pnmOutP->imageoutFileP = imageoutFileP;
+    pnmOutP->alphaFileP    = alphaFileP;
+    pnmOutP->maxval        = maxval;
+    pnmOutP->format        = format;
+    pnmOutP->alphaMaxval   = alphaMaxval;
+
+    setupFlipper(pnmOutP, orientation, flipIfNeeded, orientraw, verbose,
+                 flipOkP, noflipOkP);
+
+    computeOutputDimensions(cols, rows, orientation, orientraw,
+                            &pnmOutP->outCols, &pnmOutP->outRows, verbose);
+
+    if (pnmOutP->flipping) {
+        pnmOutP->inCols = cols;         /* Caller won't flip */
+        pnmOutP->inRows = rows;
+    } else {
+        pnmOutP->inCols = pnmOutP->outCols;  /* Caller will flip */
+        pnmOutP->inRows = pnmOutP->outRows;
+    }    
+    if (pnmOutP->flipping) {
+        if (pnmOutP->imagePipeP != NULL) 
+            pnm_writepnminit(pnmOutP->imagePipeP,
+                             pnmOutP->inCols, pnmOutP->inRows,
+                             pnmOutP->maxval, pnmOutP->format, 0);
+        if (pnmOutP->alphaPipeP != NULL) 
+            pgm_writepgminit(pnmOutP->alphaPipeP,
+                             pnmOutP->inCols, pnmOutP->inRows,
+                             pnmOutP->alphaMaxval, 0);
+    } else {
+        if (imageoutFileP != NULL) 
+            pnm_writepnminit(pnmOutP->imageoutFileP,
+                             pnmOutP->outCols, pnmOutP->outRows,
+                             pnmOutP->maxval, pnmOutP->format, 0);
+        if (alphaFileP != NULL) 
+            pgm_writepgminit(pnmOutP->alphaFileP,
+                             pnmOutP->outCols, pnmOutP->outRows,
+                             pnmOutP->alphaMaxval, 0);
+    }
+}
+
+
+
+static void
+pnmOut_term(pnmOut * const pnmOutP,
+            bool     const verbose) {
+
+    if (pnmOutP->flipping) {
+        /* Closing the pipes also causes the Pamflip processes to terminate
+           and they consequently flush their output to pnmOutP->imageoutFileP
+           and pnmOutP->alphaFileP and close those file descriptors.
+
+           We wait for the processes to exit before returning so that we
+           know everything is flushed, so the invoker of Tifftopnm is free
+           to use its output.
+        */
+        if (verbose)
+            pm_message("Flushing data through Pamflip process, "
+                       "waiting for Pamflip to terminate");
+
+        if (pnmOutP->imagePipeP) {
+            int status;
+            fclose(pnmOutP->imagePipeP);
+            waitpid(pnmOutP->imageFlipPid, &status, 0);
+        }
+        if (pnmOutP->alphaPipeP) {
+            int status;
+            fclose(pnmOutP->alphaPipeP);
+            waitpid(pnmOutP->alphaFlipPid, &status, 0);
+        }
+    } else {
+        if (pnmOutP->imageoutFileP)
+            fflush(pnmOutP->imageoutFileP);
+        if (pnmOutP->alphaFileP)
+            fflush(pnmOutP->alphaFileP);
+    }
+}
+
+
+
+static void
+pnmOut_writeRow(pnmOut *     const pnmOutP,
+                unsigned int const cols,
+                const xel *  const imageRow,
+                const gray * const alphaRow) {
+
+    assert(cols == pnmOutP->inCols);
+
+    if (pnmOutP->flipping) {
+        if (pnmOutP->imagePipeP != NULL) 
+            pnm_writepnmrow(pnmOutP->imagePipeP, (xel *)imageRow,
+                            pnmOutP->inCols, pnmOutP->maxval,
+                            pnmOutP->format, 0);
+        if (pnmOutP->alphaPipeP != NULL) 
+            pgm_writepgmrow(pnmOutP->alphaPipeP, alphaRow,
+                            pnmOutP->inCols, pnmOutP->alphaMaxval, 0);
+    } else {
+        if (pnmOutP->imageoutFileP != NULL) 
+            pnm_writepnmrow(pnmOutP->imageoutFileP, (xel *)imageRow,
+                            pnmOutP->outCols, pnmOutP->maxval,
+                            pnmOutP->format, 0);
+        if (pnmOutP->alphaFileP != NULL) 
+            pgm_writepgmrow(pnmOutP->alphaFileP, alphaRow,
+                            pnmOutP->outCols, pnmOutP->alphaMaxval, 0);
+    }
+}
+
+
+
 static void
 convertRow(unsigned int   const samplebuf[], 
            xel                  xelrow[], 
@@ -706,19 +1247,18 @@ convertMultiPlaneRow(TIFF *         const tif,
 
 
 static void
-convertRasterByRows(FILE *         const imageoutFile, 
-                    FILE *         const alphaFile,
+convertRasterByRows(pnmOut *       const pnmOutP,
                     unsigned int   const cols, 
                     unsigned int   const rows,
                     xelval         const maxval,
-                    int            const format, 
                     TIFF *         const tif,
                     unsigned short const photomet, 
                     unsigned short const planarconfig,
                     unsigned short const bps,
                     unsigned short const spp,
                     unsigned short const fillorder,
-                    xel                  colormap[]) {
+                    xel            const colormap[],
+                    bool           const verbose) {
 /*----------------------------------------------------------------------------
    With the TIFF header all processed (and relevant information from it in 
    our arguments), write out the TIFF raster to the file images *imageoutFile
@@ -735,27 +1275,30 @@ convertRasterByRows(FILE *         const imageoutFile,
         /* Same info as 'scanbuf' above, but with each raster column (sample)
            represented as single array element, so it's easy to work with.
         */
-    xel* xelrow;
+    xel * xelrow;
         /* The ppm-format row of the image row we are presently converting */
-    gray* alpharow;
+    gray * alpharow;
         /* The pgm-format row representing the alpha values for the image 
            row we are presently converting.
         */
 
     int row;
 
+    if (verbose)
+        pm_message("Converting row by row ...");
+
     scanbuf = (unsigned char *) malloc(TIFFScanlineSize(tif));
     if (scanbuf == NULL)
         pm_error("can't allocate memory for scanline buffer");
 
     MALLOCARRAY(samplebuf, cols * spp);
     if (samplebuf == NULL)
-        pm_error ("can't allocate memory for row buffer");
+        pm_error("can't allocate memory for row buffer");
 
     xelrow = pnm_allocrow(cols);
     alpharow = pgm_allocrow(cols);
 
-    for ( row = 0; row < rows; ++row ) {
+    for (row = 0; row < rows; ++row) {
         /* Read one row of samples into samplebuf[] */
 
         if (planarconfig == PLANARCONFIG_CONTIG) {
@@ -769,12 +1312,8 @@ convertRasterByRows(FILE *         const imageoutFile,
             convertMultiPlaneRow(tif, xelrow, alpharow, cols, maxval, row,
                                  photomet, bps, spp, fillorder,
                                  scanbuf, samplebuf);
-            
-        if (imageoutFile != NULL) 
-            pnm_writepnmrow( imageoutFile, 
-                             xelrow, cols, (xelval) maxval, format, 0 );
-        if (alphaFile != NULL) 
-            pgm_writepgmrow( alphaFile, alpharow, cols, (gray) maxval, 0);
+
+        pnmOut_writeRow(pnmOutP, cols, xelrow, alpharow);
     }
     pgm_freerow(alpharow);
     pnm_freerow(xelrow);
@@ -785,38 +1324,81 @@ convertRasterByRows(FILE *         const imageoutFile,
 
 
 
+static void
+warnBrokenTiffLibrary(TIFF * const tiffP) {
+
+/* TIFF library bug:
+
+   In every version of the TIFF library we've seen, TIFFRGBAImageGet()
+   fails when the raster orientation (per the TIFF_ORIENTATION tag)
+   requires a transposition, e.g. ORIENTATION_LEFTBOT.  It simply omits
+   the transposition part, so e.g. it treats ORIENTATION_LEFTBOT as
+   ORIENTATION_BOTLEFT.  And because we provide a raster buffer dimensioned
+   for the properly transposed image, the result is somewhat of a mess.
+   
+   We have found no documentation of the TIFF library that suggests
+   this behavior is as designed, so it's probably not a good idea to
+   work around it; it might be fixed somewhere.
+
+   The user can of course work around just by using -byrow and therefore
+   not using TIFFRGBAImageGet().
+
+   There is some evidence of an interface in the TIFF library that
+   lets you request that TIFFRGBAImageGet() produce a raster in the
+   same orientation as the one in the TIFF image.
+   (tiff.req_orientation).  We could conceivably use that and then do
+   a Pamflip to get the proper orientation, but that somewhat defeats
+   the philosophy of using TIFFRGBAImageGet(), so I would like to wait
+   until there's a good practical reason to do it.
+*/
+
+    unsigned short tiffOrientation;
+    int present;
+    present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+    if (present) {
+        switch (tiffOrientation) {
+        case ORIENTATION_LEFTTOP:
+        case ORIENTATION_RIGHTTOP:
+        case ORIENTATION_RIGHTBOT:
+        case ORIENTATION_LEFTBOT:
+            pm_message("WARNING: This TIFF image has an orientation that "
+                       "most TIFF libraries converts incorrectly.  "
+                       "Use -byrow to circumvent.");
+            break;
+        }
+    }
+}
+
+
 
 static void 
 convertTiffRaster(uint32 *        const raster, 
                   unsigned int    const cols,
                   unsigned int    const rows,
                   xelval          const maxval,
-                  int             const format,
-                  FILE *          const imageoutFile,
-                  FILE *          const alphaFile) {
+                  pnmOut *        const pnmOutP) {
 /*----------------------------------------------------------------------------
    Convert the raster 'raster' from the format generated by the TIFF library
    to PPM (plus PGM alpha mask where applicable) and output it to
-   the files *imageoutFile and *alphaFile in format 'format' with maxval
-   'maxval'.  The raster is 'cols' wide by 'rows' high.
+   the object *pnmOutP.  The raster is 'cols' wide by 'rows' high.
 -----------------------------------------------------------------------------*/
-    xel* xelrow;
+    xel * xelrow;
         /* The ppm-format row of the image row we are
            presently converting 
         */
-    gray* alpharow;
+    gray * alpharow;
         /* The pgm-format row representing the alpha values
            for the image row we are presently converting.  
         */
-    int row;
+    unsigned int row;
 
     xelrow = pnm_allocrow(cols);
     alpharow = pgm_allocrow(cols);
 
     for (row = 0; row < rows; ++row) {
-        uint32* rp;  
+        uint32 * rp;  
             /* Address of pixel in 'raster' we are presently converting */
-        int col;
+        unsigned int col;
 
         /* Start at beginning of row: */
         rp = raster + (rows - row - 1) * cols;
@@ -830,11 +1412,7 @@ convertTiffRaster(uint32 *        const raster,
                        TIFFGetB(tiffPixel) * maxval / 255);
             alpharow[col] = TIFFGetA(tiffPixel) * maxval / 255 ;
         }
-        
-        if (imageoutFile != NULL) 
-            pnm_writepnmrow(imageoutFile, xelrow, cols, maxval, format, 0);
-        if (alphaFile != NULL) 
-            pgm_writepgmrow(alphaFile, alpharow, cols, maxval, 0);
+        pnmOut_writeRow(pnmOutP, cols, xelrow, alpharow);
     }
     
     pgm_freerow(alpharow);
@@ -847,45 +1425,54 @@ enum convertDisp {CONV_DONE, CONV_OOM, CONV_UNABLE, CONV_FAILED,
                   CONV_NOTATTEMPTED};
 
 static void
-convertRasterInMemory(FILE *         const imageoutFile, 
-                      FILE *         const alphaFile,
-                      unsigned int   const cols, 
-                      unsigned int   const rows,
+convertRasterInMemory(pnmOut *       const pnmOutP,
                       xelval         const maxval,
-                      int            const format, 
                       TIFF *         const tif,
                       unsigned short const photomet, 
                       unsigned short const planarconfig,
                       unsigned short const bps,
                       unsigned short const spp,
                       unsigned short const fillorder,
-                      xel                  colormap[],
+                      xel            const colormap[],
+                      bool           const verbose,
                       enum convertDisp * const statusP) {
 /*----------------------------------------------------------------------------
-   With the TIFF header all processed (and relevant information from it in 
-   our arguments), write out the TIFF raster to the file images *imageoutFile
-   and *alphaFile.
+   With the TIFF header all processed (and relevant information from
+   it in our arguments), write out the TIFF raster to the file images
+   *imageoutFileP and *alphaFileP.
 
    Do this by reading the entire TIFF image into memory at once and formatting
    it with the TIFF library's TIFFRGBAImageGet().
 
+   'cols' and 'rows' are the dimensions of the actual image, not of the
+   TIFF raster matrix; ('tif' knows the TIFF raster matrix dimensions).
+
    Return *statusP == CONV_OOM iff we are unable to proceed because we cannot
    get memory to store the entire raster.  This means Caller may still be able
    to do the conversion using a row-by-row strategy.  Like typical Netpbm
    programs, we simply abort the program if we are unable to allocate
    memory for other things.
 -----------------------------------------------------------------------------*/
+    unsigned int cols, rows;  /* Dimensions of output image */
+
+    if (verbose)
+        pm_message("Converting in memory ...");
+
+    warnBrokenTiffLibrary(tif);
+
+    getTiffDimensions(tif, &cols, &rows);
+
     if (rows == 0 || cols == 0) 
         *statusP = CONV_DONE;
     else {
-        char emsg[1024] ;
+        char emsg[1024];
         int ok;
         ok = TIFFRGBAImageOK(tif, emsg);
         if (!ok) {
             pm_message(emsg);
             *statusP = CONV_UNABLE;
         } else {
-            uint32* raster ;
+            uint32 * raster;
 
             /* Note that TIFFRGBAImageGet() converts any bits per sample
                to 8.  Maxval of the raster it returns is always 255.
@@ -897,10 +1484,10 @@ convertRasterInMemory(FILE *         const imageoutFile,
                 *statusP = CONV_OOM;
             } else {
                 int const stopOnErrorFalse = FALSE;
-                TIFFRGBAImage img ;
+                TIFFRGBAImage img;
                 int ok;
                 
-                ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg) ;
+                ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg);
                 if (!ok) {
                     pm_message(emsg);
                     *statusP = CONV_FAILED;
@@ -913,8 +1500,7 @@ convertRasterInMemory(FILE *         const imageoutFile,
                         *statusP = CONV_FAILED;
                     } else {
                         *statusP = CONV_DONE;
-                        convertTiffRaster(raster, cols, rows, maxval, format, 
-                                          imageoutFile, alphaFile);
+                        convertTiffRaster(raster, cols, rows, maxval, pnmOutP);
                     }
                 } 
                 free(raster);
@@ -926,62 +1512,86 @@ convertRasterInMemory(FILE *         const imageoutFile,
 
 
 static void
+convertRaster(pnmOut *           const pnmOutP,
+              TIFF *             const tifP,
+              struct tiffDirInfo const tiffDir,
+              xelval             const maxval,
+              unsigned short     const fillorder,
+              const xel *        const colormap,
+              bool               const byrow,
+              bool               const flipOk,
+              bool               const noflipOk,
+              bool               const verbose) {
+
+    enum convertDisp status;
+    if (byrow || !flipOk)
+        status = CONV_NOTATTEMPTED;
+    else {
+        convertRasterInMemory(
+            pnmOutP, maxval,
+            tifP, tiffDir.photomet, tiffDir.planarconfig, 
+            tiffDir.bps, tiffDir.spp, fillorder,
+            colormap, verbose, &status);
+    }
+    if (status == CONV_DONE) {
+        if (tiffDir.bps > 8)
+            pm_message("actual resolution has been reduced to 24 bits "
+                       "per pixel in the conversion.  You can get the "
+                       "full %u bits that are in the TIFF with the "
+                       "-byrow option.", tiffDir.bps);
+    } else {
+        if (status != CONV_NOTATTEMPTED) {
+            pm_message("In-memory conversion failed; "
+                       "using more primitive row-by-row conversion.");
+
+            if (!noflipOk)
+                pm_error("TIFF raster is in nonstandard orientation, "
+                         "and we already committed to in-memory "
+                         "conversion.  To avoid this failure, "
+                         "use -byrow .");
+        }            
+        convertRasterByRows(
+            pnmOutP, tiffDir.width, tiffDir.height, maxval,
+            tifP, tiffDir.photomet, tiffDir.planarconfig,
+            tiffDir.bps, tiffDir.spp, fillorder, colormap, verbose);
+    }
+}
+
+
+
+static void
 convertImage(TIFF *             const tifP,
-             FILE *             const alphaFile, 
-             FILE *             const imageoutFile,
+             FILE *             const alphaFileP,
+             FILE *             const imageoutFileP,
              struct cmdlineInfo const cmdline) {
 
-    unsigned int cols, rows;
+    struct tiffDirInfo tiffDir;
     int format;
     xelval maxval;
-    unsigned short bps, spp;
-
     xel colormap[MAXCOLORS];
-    unsigned short photomet, planarconfig, fillorderTag;
     unsigned short fillorder;
+    bool flipOk, noflipOk;
+    pnmOut pnmOut;
 
-    read_directory(tifP, &bps, &spp, &photomet, &planarconfig, &fillorderTag,
-                   &cols, &rows, 
-                   cmdline.headerdump);
-
+    readDirectory(tifP, cmdline.headerdump, &tiffDir);
 
-    computeFillorder(fillorderTag, &fillorder, cmdline.respectfillorder);
+    computeFillorder(tiffDir.fillorder, &fillorder, cmdline.respectfillorder);
 
-    analyzeImageType(tifP, bps, spp, photomet, 
+    analyzeImageType(tifP, tiffDir.bps, tiffDir.spp, tiffDir.photomet, 
                      &maxval, &format, colormap, cmdline.headerdump, cmdline);
 
-    if (imageoutFile != NULL) 
-        pnm_writepnminit( imageoutFile, 
-                          cols, rows, (xelval) maxval, format, 0 );
-    if (alphaFile != NULL) 
-        pgm_writepgminit( alphaFile, cols, rows, (gray) maxval, 0 );
+    pnmOut_init(imageoutFileP, alphaFileP, tiffDir.width, tiffDir.height,
+                tiffDir.orientation, maxval, format, maxval,
+                cmdline.byrow, cmdline.orientraw,
+                cmdline.verbose,
+                &flipOk, &noflipOk,
+                &pnmOut);
 
-    {
-        enum convertDisp status;
-        if (cmdline.byrow)
-            status = CONV_NOTATTEMPTED;
-        else {
-            convertRasterInMemory(
-                imageoutFile, alphaFile, cols, rows, maxval, format, 
-                tifP, photomet, planarconfig, bps, spp, fillorder,
-                colormap, &status);
-        }
-        if (status == CONV_DONE) {
-            if (bps > 8)
-                pm_message("actual resolution has been reduced to 24 bits "
-                           "per pixel in the conversion.  You can get the "
-                           "full %u bits that are in the TIFF with the "
-                           "-byrow option.", bps);
-        } else {
-            if (status != CONV_NOTATTEMPTED)
-                pm_message("In-memory conversion failed; "
-                           "using more primitive row-by-row conversion.");
-            
-            convertRasterByRows(
-                imageoutFile, alphaFile, cols, rows, maxval, format, 
-                tifP, photomet, planarconfig, bps, spp, fillorder, colormap);
-        }
-    }
+    convertRaster(&pnmOut, tifP, tiffDir, maxval,
+                  fillorder, colormap, cmdline.byrow, flipOk, noflipOk,
+                  cmdline.verbose);
+
+    pnmOut_term(&pnmOut, cmdline.verbose);
 }
 
 
@@ -1013,18 +1623,18 @@ convertIt(TIFF *             const tifP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
     TIFF * tif;
     FILE * alphaFile;
     FILE * imageoutFile;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if (!STREQ(cmdline.inputFilename, "-")) {
+    if (!streq(cmdline.inputFilename, "-")) {
         tif = TIFFOpen(cmdline.inputFilename, "r");
         if (tif == NULL)
             pm_error("error opening TIFF file %s", cmdline.inputFilename);
@@ -1056,7 +1666,6 @@ main(int argc, char * argv[]) {
     strfree(cmdline.inputFilename);
 
     /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
+       code, via various function calls.  */
     return 0;
 }
diff --git a/converter/other/x11wd.h b/converter/other/x11wd.h
index 711248f5..7161260b 100644
--- a/converter/other/x11wd.h
+++ b/converter/other/x11wd.h
@@ -27,7 +27,7 @@ enum visualclass {StaticGray=0,GrayScale=1,StaticColor=2,PseudoColor=3,
 #define DirectColor 5
 */
 
-typedef uint32n xwdval;
+typedef uint32_t xwdval;
 #define XWDVAL_MAX ((xwdval)(-1))
 #define X11WD_FILE_VERSION 7
 typedef struct {
@@ -67,13 +67,13 @@ typedef struct {
         */
     xwdval window_width;    /* Window width */
     xwdval window_height;   /* Window height */
-    int32n window_x;        /* Window upper left X coordinate */
-    int32n window_y;        /* Window upper left Y coordinate */
+    int32_t window_x;        /* Window upper left X coordinate */
+    int32_t window_y;        /* Window upper left Y coordinate */
     xwdval window_bdrwidth; /* Window border width */
     } X11WDFileHeader;
 
 typedef struct {
-    uint32n num;
+    uint32_t num;
     unsigned short red, green, blue;
     char flags;         /* do_red, do_green, do_blue */
     char pad;
diff --git a/converter/other/xwdtopnm.c b/converter/other/xwdtopnm.c
index 284d0204..2cf1d39b 100644
--- a/converter/other/xwdtopnm.c
+++ b/converter/other/xwdtopnm.c
@@ -14,6 +14,12 @@
 
    The file X11/XWDFile.h from the X Window System is an authority for the
    format of an XWD file.  Netpbm uses its own declaration, though.
+
+   It has been a real challenge trying to reverse engineer the XWD
+   format.  This program is almost always broken as people find XWD images
+   with which it does not work and we update the program in response.
+
+   We consider an XWD file correct if Xwud displays it properly.
 */
 
 
@@ -32,14 +38,22 @@
 #include "x10wd.h"
 #include "x11wd.h"
 
+struct compMask {
+    unsigned long red;
+    unsigned long grn;
+    unsigned long blu;
+};
+
+
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filename;
+    const char * inputFilename;
     unsigned int verbose;
     unsigned int debug;
     unsigned int headerdump;
+    unsigned int cmapdump;
 };
 
 
@@ -70,23 +84,6 @@ zero_bits(const unsigned long mask) {
 
 
 
-static int
-one_bits(const unsigned long input) {
-/*----------------------------------------------------------------------------
-   Return the number of one bits in the binary representation of 'input'.
------------------------------------------------------------------------------*/
-    int one_bits;
-    unsigned long mask;
-
-    one_bits = 0;   /* initial value */
-    for (mask = 0x00000001; mask != 0x00000000; mask <<= 1)
-        if (input & mask) one_bits++;
-
-    return(one_bits);
-}
-
-
-
 static void
 parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -110,6 +107,7 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
     OPTENT3(0,   "debug",      OPT_FLAG,   NULL, &cmdlineP->debug,         0);
     OPTENT3(0,   "headerdump", OPT_FLAG,   NULL, &cmdlineP->headerdump,    0);
+    OPTENT3(0,   "cmapdump",   OPT_FLAG,   NULL, &cmdlineP->cmapdump,      0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -119,12 +117,12 @@ parseCommandLine(int argc, char ** argv,
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc - 1 == 0)
-        cmdlineP->input_filename = NULL;  /* he wants stdin */
+        cmdlineP->inputFilename = NULL;  /* he wants stdin */
     else if (argc - 1 == 1) {
-        if (STREQ(argv[1], "-"))
-            cmdlineP->input_filename = NULL;  /* he wants stdin */
+        if (streq(argv[1], "-"))
+            cmdlineP->inputFilename = NULL;  /* he wants stdin */
         else 
-            cmdlineP->input_filename = strdup(argv[1]);
+            cmdlineP->inputFilename = strdup(argv[1]);
     } else 
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
@@ -137,16 +135,14 @@ processX10Header(X10WDFileHeader *  const h10P,
                  FILE *             const file,
                  int *              const colsP, 
                  int *              const rowsP, 
-                 int *              const padrightP, 
+                 unsigned int *     const padrightP, 
                  xelval *           const maxvalP, 
                  enum visualclass * const visualclassP, 
                  int *              const formatP, 
                  xel **             const colorsP, 
                  int *              const bits_per_pixelP, 
                  int *              const bits_per_itemP, 
-                 unsigned long *    const red_maskP, 
-                 unsigned long *    const green_maskP, 
-                 unsigned long *    const blue_maskP,
+                 struct compMask *  const compMaskP,
                  enum byteorder *   const byte_orderP,
                  enum byteorder *   const bit_orderP) {
 
@@ -155,9 +151,7 @@ processX10Header(X10WDFileHeader *  const h10P,
     bool grayscale;
     bool byte_swap;
 
-    *maxvalP = 65535;   /* Initial assumption */
-
-    if ( h10P->file_version != X10WD_FILE_VERSION ) {
+    if (h10P->file_version != X10WD_FILE_VERSION) {
         byte_swap = TRUE;
         h10P->header_size     = pm_bs_long(h10P->header_size);
         h10P->file_version    = pm_bs_long(h10P->file_version);
@@ -175,51 +169,53 @@ processX10Header(X10WDFileHeader *  const h10P,
     } else
         byte_swap = FALSE;
 
-    for ( i = 0; i < h10P->header_size - sizeof(*h10P); ++i )
-        if ( getc( file ) == EOF )
-            pm_error( "couldn't read rest of X10 XWD file header" );
+    for (i = 0; i < h10P->header_size - sizeof(*h10P); ++i)
+        if (getc(file) == EOF)
+            pm_error("couldn't read rest of X10 XWD file header");
 
     /* Check whether we can handle this dump. */
-    if ( h10P->window_ncolors > 256 )
-        pm_error( "can't handle X10 window_ncolors > %d", 256 );
-    if ( h10P->pixmap_format != ZFormat && h10P->display_planes != 1 )
-        pm_error(
-            "can't handle X10 pixmap_format %d with planes != 1",
-            h10P->pixmap_format );
+    if (h10P->window_ncolors > 256)
+        pm_error("can't handle X10 window_ncolors > %d", 256);
+    if (h10P->pixmap_format != ZFormat && h10P->display_planes != 1)
+        pm_error("can't handle X10 pixmap_format %d with planes != 1",
+                 h10P->pixmap_format);
 
     grayscale = TRUE;  /* initial assumption */
-    if ( h10P->window_ncolors != 0 ) {
+    if (h10P->window_ncolors != 0) {
         /* Read X10 colormap. */
-        MALLOCARRAY( x10colors, h10P->window_ncolors );
-        if ( x10colors == NULL )
-            pm_error( "out of memory" );
-        for ( i = 0; i < h10P->window_ncolors; ++i ) {
-            if ( fread( &x10colors[i], sizeof(X10Color), 1, file ) != 1 )
-                pm_error( "couldn't read X10 XWD colormap" );
-            if ( byte_swap ) {
+        MALLOCARRAY(x10colors, h10P->window_ncolors);
+        if (x10colors == NULL)
+            pm_error("out of memory");
+        for (i = 0; i < h10P->window_ncolors; ++i) {
+            size_t bytesRead;
+            bytesRead = fread(&x10colors[i], sizeof(X10Color), 1, file);
+            if (bytesRead != 1)
+                pm_error("couldn't read X10 XWD colormap");
+            if (byte_swap) {
                 x10colors[i].red   = pm_bs_short(x10colors[i].red);
                 x10colors[i].green = pm_bs_short(x10colors[i].green);
                 x10colors[i].blue  = pm_bs_short(x10colors[i].blue);
             }
-            if ( x10colors[i].red != x10colors[i].green ||
-                 x10colors[i].green != x10colors[i].blue )
+            if (x10colors[i].red != x10colors[i].green ||
+                x10colors[i].green != x10colors[i].blue)
                 grayscale = FALSE;
         }
     }
 
-    if ( h10P->display_planes == 1 ) {
+    if (h10P->display_planes == 1) {
         *formatP = PBM_TYPE;
         *visualclassP = StaticGray;
         *maxvalP = 1;
-        *colorsP = pnm_allocrow( 2 );
-        PNM_ASSIGN1( (*colorsP)[0], 0 );
-        PNM_ASSIGN1( (*colorsP)[1], *maxvalP );
+        *colorsP = pnm_allocrow(2);
+        PNM_ASSIGN1((*colorsP)[0], 0);
+        PNM_ASSIGN1((*colorsP)[1], *maxvalP);
         *padrightP =
             (((h10P->pixmap_width + 15) / 16) * 16 - h10P->pixmap_width) * 8;
         *bits_per_itemP = 16;
         *bits_per_pixelP = 1;
-    } else if ( h10P->window_ncolors == 0 ) { 
+    } else if (h10P->window_ncolors == 0) { 
         /* Must be grayscale. */
+        unsigned int i;
         *formatP = PGM_TYPE;
         *visualclassP = StaticGray;
         if (h10P->display_planes > sizeof(*maxvalP) * 8 - 1)
@@ -231,23 +227,27 @@ processX10Header(X10WDFileHeader *  const h10P,
             pm_error("XWD header says display_planes = %u, which is too "
                      "large for maximum maxval of %u",
                      h10P->display_planes, PNM_OVERALLMAXVAL);
-        *colorsP = pnm_allocrow( *maxvalP + 1 );
-        for ( i = 0; i <= *maxvalP; ++i )
-            PNM_ASSIGN1( (*colorsP)[i], i );
+        *colorsP = pnm_allocrow(*maxvalP + 1);
+        for (i = 0; i <= *maxvalP; ++i)
+            PNM_ASSIGN1((*colorsP)[i], i);
         *padrightP =
             (((h10P->pixmap_width + 15) / 16) * 16 - h10P->pixmap_width) * 8;
         *bits_per_itemP = 16;
         *bits_per_pixelP = 1;
     } else {
-        *colorsP = pnm_allocrow( h10P->window_ncolors );
+        *maxvalP = 65535;
+
+        *colorsP = pnm_allocrow(h10P->window_ncolors);
         *visualclassP = PseudoColor;
-        if ( grayscale ) {
+        if (grayscale) {
+            unsigned int i;
             *formatP = PGM_TYPE;
-            for ( i = 0; i < h10P->window_ncolors; ++i )
-                PNM_ASSIGN1( (*colorsP)[i], x10colors[i].red );
+            for (i = 0; i < h10P->window_ncolors; ++i)
+                PNM_ASSIGN1((*colorsP)[i], x10colors[i].red);
         } else {
+            unsigned int i;
             *formatP = PPM_TYPE;
-            for ( i = 0; i < h10P->window_ncolors; ++i )
+            for (i = 0; i < h10P->window_ncolors; ++i)
                 PPM_ASSIGN(
                     (*colorsP)[i], x10colors[i].red, x10colors[i].green,
                     x10colors[i].blue);
@@ -308,36 +308,46 @@ fixH11ByteOrder(X11WDFileHeader *  const h11P,
 
 
 static void
-readX11Colormap(FILE *      const file,
-                int         const ncolors, 
-                bool        const byteSwap,
-                X11XColor** const x11colorsP) {
+dumpX11Cmap(unsigned int       const nColors,
+            const X11XColor *  const x11colors) {
+
+    unsigned int i;
+    for (i = 0; i < nColors; ++i)
+        pm_message("Color %u r/g/b = %u/%u/%u", i, 
+                   x11colors[i].red, x11colors[i].green, 
+                   x11colors[i].blue);
+}
+
+
+
+static void
+readX11Colormap(FILE *       const file,
+                unsigned int const nColors, 
+                bool         const byteSwap,
+                bool         const cmapDump,
+                X11XColor**  const x11colorsP) {
                 
     X11XColor * x11colors;
     int rc;
 
     /* Read X11 colormap. */
-    MALLOCARRAY(x11colors, ncolors);
+    MALLOCARRAY(x11colors, nColors);
     if (x11colors == NULL)
         pm_error("out of memory");
-    rc = fread(x11colors, sizeof(x11colors[0]), ncolors, file);
-    if (rc != ncolors)
+    rc = fread(x11colors, sizeof(x11colors[0]), nColors, file);
+    if (rc != nColors)
         pm_error("couldn't read X11 XWD colormap");
     if (byteSwap) {
         unsigned int i;
-        for (i = 0; i < ncolors; ++i) {
+        for (i = 0; i < nColors; ++i) {
             x11colors[i].red   = pm_bs_short(x11colors[i].red);
             x11colors[i].green = pm_bs_short(x11colors[i].green);
             x11colors[i].blue  = pm_bs_short(x11colors[i].blue);
         }
     }
-    if (debug) {
-        unsigned int i;
-        for (i = 0; i < ncolors && i < 8; ++i)
-            pm_message("Color %d r/g/b = %d/%d/%d", i, 
-                       x11colors[i].red, x11colors[i].green, 
-                       x11colors[i].blue);
-    }
+    if (cmapDump)
+        dumpX11Cmap(nColors, x11colors);
+
     *x11colorsP = x11colors;
 }
 
@@ -456,9 +466,7 @@ reverseBits(unsigned long arg,
 
 static void
 computeComponentMasks(X11WDFileHeader * const h11P,
-                      unsigned long *   const redMaskP,
-                      unsigned long *   const grnMaskP,
-                      unsigned long *   const bluMaskP) {
+                      struct compMask * const compMaskP) {
 /*----------------------------------------------------------------------------
    You'd think the component (red, green, blue) masks in the header
    would just be right.  But we've seen a direct color image which has
@@ -470,33 +478,57 @@ computeComponentMasks(X11WDFileHeader * const h11P,
 -----------------------------------------------------------------------------*/
     if (h11P->visual_class == DirectColor &&
         h11P->bits_per_pixel == 24 && h11P->bitmap_bit_order == LSBFirst) {
-        *redMaskP = reverseBits(h11P->red_mask, 24);
-        *grnMaskP = reverseBits(h11P->green_mask, 24);
-        *bluMaskP = reverseBits(h11P->blue_mask, 24);
+        compMaskP->red = reverseBits(h11P->red_mask, 24);
+        compMaskP->grn = reverseBits(h11P->green_mask, 24);
+        compMaskP->blu = reverseBits(h11P->blue_mask, 24);
     } else {
-        *redMaskP = h11P->red_mask;
-        *grnMaskP = h11P->green_mask;
-        *bluMaskP = h11P->blue_mask;
+        compMaskP->red = h11P->red_mask;
+        compMaskP->grn = h11P->green_mask;
+        compMaskP->blu = h11P->blue_mask;
     }
 }
 
 
+/* About TrueColor maxval:
+
+   The X11 spec says that in TrueColor, you use the bits in the raster for a
+   particular color component of a particular pixel to index the server's
+   colormap for that component, which contains 'bits_per_rgb' significant bits
+   of intensity information.  'bits_per_rgb' is in the XWD header, and in
+   practice is normally 8 or 16, usually 8.
+
+   We don't have the server's colormap, so we assume the most ordinary
+   one, a linear-as-possible distribution over the indices.
+
+   That means the maxval is that implied by 'bits_per_rgb' bits and we get
+   the proper sample value by scaling the value from the raster to that
+   maxval.
+
+   We (mostly Julian Bradfield <jcb@inf.ed.ac.uk>) figured this out in Netpbm
+   10.46 (March 2009).  Between ca. 2000 and 10.46, we instead assumed the
+   value in the XWD raster to be the exact brightness value, and chose a
+   maxval that would best allow us to represent that exact value for all
+   three components (e.g. if the XWD had 5 bits for blue, 5 for red, and
+   6 for red, we'd use maxval 31*63=1953).  Before that, the maxval was
+   31 if bits per pixel was 16 and 255 otherwise.
+*/
+
+
 
 static void
 processX11Header(X11WDFileHeader *  const h11P, 
-                 FILE *             const file,
+                 FILE *             const fileP,
+                 bool               const cmapDump,
                  int *              const colsP, 
                  int *              const rowsP, 
-                 int *              const padrightP, 
+                 unsigned int *     const padrightP, 
                  xelval *           const maxvalP, 
                  enum visualclass * const visualclassP, 
                  int *              const formatP, 
                  xel **             const colorsP, 
                  int *              const bits_per_pixelP, 
                  int *              const bits_per_itemP, 
-                 unsigned long *    const red_maskP, 
-                 unsigned long *    const green_maskP, 
-                 unsigned long *    const blue_maskP,
+                 struct compMask *  const compMaskP,
                  enum byteorder *   const byte_orderP,
                  enum byteorder *   const bit_orderP) {
 
@@ -512,38 +544,36 @@ processX11Header(X11WDFileHeader *  const h11P,
         pm_message("Header is different endianness from this machine.");
     
     for (i = 0; i < h11FixedP->header_size - sizeof(*h11FixedP); ++i)
-        if (getc(file) == EOF)
+        if (getc(fileP) == EOF)
             pm_error("couldn't read rest of X11 XWD file header");
 
     /* Check whether we can handle this dump. */
-    if ( h11FixedP->pixmap_depth > 24 )
-        pm_error( "can't handle X11 pixmap_depth > 24" );
-    if ( h11FixedP->bits_per_rgb > 24 )
-        pm_error( "can't handle X11 bits_per_rgb > 24" );
-    if ( h11FixedP->pixmap_format != ZPixmap && h11FixedP->pixmap_depth != 1 )
-        pm_error(
-            "can't handle X11 pixmap_format %d with depth != 1",
-            h11FixedP->pixmap_format );
-    if ( h11FixedP->bitmap_unit != 8 && h11FixedP->bitmap_unit != 16 &&
-         h11FixedP->bitmap_unit != 32 )
-        pm_error(
-            "X11 bitmap_unit (%d) is non-standard - can't handle",
-            h11FixedP->bitmap_unit );
+    if (h11FixedP->pixmap_depth > 24)
+        pm_error( "can't handle X11 pixmap_depth > 24");
+    if (h11FixedP->bits_per_rgb > 24)
+        pm_error("can't handle X11 bits_per_rgb > 24");
+    if (h11FixedP->pixmap_format != ZPixmap && h11FixedP->pixmap_depth != 1)
+        pm_error("can't handle X11 pixmap_format %d with depth != 1",
+                 h11FixedP->pixmap_format);
+    if (h11FixedP->bitmap_unit != 8 && h11FixedP->bitmap_unit != 16 &&
+        h11FixedP->bitmap_unit != 32)
+        pm_error("X11 bitmap_unit (%d) is non-standard - can't handle",
+                 h11FixedP->bitmap_unit);
     /* The following check was added in 10.19 (November 2003) */
-    if ( h11FixedP->bitmap_pad != 8 && h11FixedP->bitmap_pad != 16 &&
-         h11FixedP->bitmap_pad != 32 )
-        pm_error(
-            "X11 bitmap_pad (%d) is non-standard - can't handle",
-            h11FixedP->bitmap_unit );
-
-    if ( h11FixedP->ncolors > 0 ) {
-        readX11Colormap( file, h11FixedP->ncolors, byte_swap, &x11colors );
-        grayscale = colormapAllGray( x11colors, h11FixedP->ncolors );
+    if (h11FixedP->bitmap_pad != 8 && h11FixedP->bitmap_pad != 16 &&
+        h11FixedP->bitmap_pad != 32)
+        pm_error("X11 bitmap_pad (%d) is non-standard - can't handle",
+                 h11FixedP->bitmap_unit);
+
+    if (h11FixedP->ncolors > 0) {
+        readX11Colormap(fileP, h11FixedP->ncolors, byte_swap, cmapDump,
+                        &x11colors);
+        grayscale = colormapAllGray(x11colors, h11FixedP->ncolors);
     } else
         grayscale = TRUE;
 
     *visualclassP = (enum visualclass) h11FixedP->visual_class;
-    if ( *visualclassP == DirectColor ) {
+    if (*visualclassP == DirectColor) {
         unsigned int i;
         *formatP = PPM_TYPE;
         *maxvalP = 65535;
@@ -553,27 +583,23 @@ processX11Header(X11WDFileHeader *  const h11P,
           is composed of 3 separate indices.
         */
 
-        *colorsP = pnm_allocrow( h11FixedP->ncolors );
-        for ( i = 0; i < h11FixedP->ncolors; ++i )
+        *colorsP = pnm_allocrow(h11FixedP->ncolors);
+        for (i = 0; i < h11FixedP->ncolors; ++i)
             PPM_ASSIGN(
                 (*colorsP)[i], x11colors[i].red, x11colors[i].green,
                 x11colors[i].blue);
-    } else if ( *visualclassP == TrueColor ) {
+    } else if (*visualclassP == TrueColor) {
         *formatP = PPM_TYPE;
 
-        *maxvalP = pm_lcm(pm_bitstomaxval(one_bits(h11FixedP->red_mask)),
-                          pm_bitstomaxval(one_bits(h11FixedP->green_mask)),
-                          pm_bitstomaxval(one_bits(h11FixedP->blue_mask)),
-                          PPM_OVERALLMAXVAL
-            );
-    }
-    else if ( *visualclassP == StaticGray && h11FixedP->bits_per_pixel == 1 ) {
+        /* See discussion above about this maxval */
+        *maxvalP = pm_bitstomaxval(h11FixedP->bits_per_rgb);
+    } else if (*visualclassP == StaticGray && h11FixedP->bits_per_pixel == 1) {
         *formatP = PBM_TYPE;
         *maxvalP = 1;
         *colorsP = pnm_allocrow( 2 );
-        PNM_ASSIGN1( (*colorsP)[0], *maxvalP );
-        PNM_ASSIGN1( (*colorsP)[1], 0 );
-    } else if ( *visualclassP == StaticGray ) {
+        PNM_ASSIGN1((*colorsP)[0], *maxvalP);
+        PNM_ASSIGN1((*colorsP)[1], 0);
+    } else if (*visualclassP == StaticGray) {
         unsigned int i;
         *formatP = PGM_TYPE;
         if (h11FixedP->bits_per_pixel > sizeof(*maxvalP) * 8 - 1)
@@ -585,20 +611,20 @@ processX11Header(X11WDFileHeader *  const h11P,
             pm_error("XWD header says bits_per_pixel = %u, which is too "
                      "large for maximum maxval of %u",
                      h11FixedP->bits_per_pixel, PNM_OVERALLMAXVAL);
-        *colorsP = pnm_allocrow( *maxvalP + 1 );
-        for ( i = 0; i <= *maxvalP; ++i )
-            PNM_ASSIGN1( (*colorsP)[i], i );
+        *colorsP = pnm_allocrow(*maxvalP + 1);
+        for (i = 0; i <= *maxvalP; ++i)
+            PNM_ASSIGN1((*colorsP)[i], i);
     } else {
-        *colorsP = pnm_allocrow( h11FixedP->ncolors );
-        if ( grayscale ) {
+        *colorsP = pnm_allocrow(h11FixedP->ncolors);
+        if (grayscale) {
             unsigned int i;
             *formatP = PGM_TYPE;
-            for ( i = 0; i < h11FixedP->ncolors; ++i )
-                PNM_ASSIGN1( (*colorsP)[i], x11colors[i].red );
+            for (i = 0; i < h11FixedP->ncolors; ++i)
+                PNM_ASSIGN1((*colorsP)[i], x11colors[i].red);
         } else {
             unsigned int i;
             *formatP = PPM_TYPE;
-            for ( i = 0; i < h11FixedP->ncolors; ++i )
+            for (i = 0; i < h11FixedP->ncolors; ++i)
                 PPM_ASSIGN(
                     (*colorsP)[i], x11colors[i].red, x11colors[i].green,
                     x11colors[i].blue);
@@ -611,11 +637,12 @@ processX11Header(X11WDFileHeader *  const h11P,
     *padrightP =
         h11FixedP->bytes_per_line * 8 -
         h11FixedP->pixmap_width * h11FixedP->bits_per_pixel;
+
     /* According to X11/XWDFile.h, the item size is 'bitmap_pad' for some
        images and 'bitmap_unit' for others.  This is strange, so there may
        be some subtlety of their definitions that we're missing.
 
-       See comments in getpix() about what an item is.
+       See comments in pixelReader_getpix() about what an item is.
 
        Ben Kelley in January 2002 had a 32 bits-per-pixel xwd file
        from a truecolor 32 bit window on a Hummingbird Exceed X server
@@ -625,25 +652,25 @@ processX11Header(X11WDFileHeader *  const h11P,
        bit-per-pixel direct color window that had bitmap_unit = 32 and
        bitmap_pad = 8.  This was made by Xwd in Red Hat Xfree86 4.3.0-2.
 
-       In March 2007, Darren Frith present an xwd file like this:
+       In March 2007, Darren Frith presented an xwd file like this:
        Header says direct color, bits_per_pixel = 24, bitmap_unit =
        32, bitmap_pad = 8, byte order and bit order LSB first.  The
        bytes in each item are in fact MSB first and the pixels spread
        across the items MSB first.  The raster is consecutive 24 bit
        pixel units, but each row is padded on the right with enough
        bits to make the total line size 32 x width.  Really strange.
-       Xwud, ImageMagick, and Gimp render this image
-       correctly, so it's not broken.
+       The header says the bits within each pixel are one byte red,
+       one byte green, one byte blue.  But they are actually blue,
+       green, red.  Xwud, ImageMagick, and Gimp render this image
+       correctly, so it's not broken.  Created by Xwd of X.org 7.1.1.
 
        Before Netpbm 9.23 (January 2002), we used bitmap_unit as the
        item size always.  Then, until 10.19 (November 2003), we used
        bitmap_pad when pixmap_depth > 1 and pixmap_format == ZPixmap.
        We still don't see any logic in these fields at all, but we
        figure whichever one is greater (assuming both are meaningful)
-       has to be the item size.  
-    */
-    *bits_per_itemP = MAX(h11FixedP->bitmap_pad, h11FixedP->bitmap_unit);
-
+       has to be the item size.  */
+    *bits_per_itemP  = MAX(h11FixedP->bitmap_pad, h11FixedP->bitmap_unit);
     *bits_per_pixelP = h11FixedP->bits_per_pixel;
 
     if (*visualclassP == DirectColor) {
@@ -656,7 +683,7 @@ processX11Header(X11WDFileHeader *  const h11P,
         *byte_orderP = (enum byteorder) h11FixedP->byte_order;
         *bit_orderP  = (enum byteorder) h11FixedP->bitmap_bit_order;
     }
-    computeComponentMasks(h11FixedP, red_maskP, green_maskP, blue_maskP);
+    computeComponentMasks(h11FixedP, compMaskP);
 
     free(h11FixedP);
 } 
@@ -667,29 +694,28 @@ static void
 getinit(FILE *             const ifP, 
         int *              const colsP, 
         int *              const rowsP, 
-        int *              const padrightP, 
+        unsigned int *     const padrightP, 
         xelval *           const maxvalP, 
         enum visualclass * const visualclassP, 
         int *              const formatP, 
         xel **             const colorsP,
         int *              const bits_per_pixelP, 
         int *              const bits_per_itemP, 
-        unsigned long *    const red_maskP, 
-        unsigned long *    const green_maskP,
-        unsigned long *    const blue_maskP,
+        struct compMask *  const compMaskP,
         enum byteorder *   const byte_orderP,
         enum byteorder *   const bit_orderP,
-        bool               const headerDump) {
+        bool               const headerDump,
+        bool               const cmapDump) {
 /*----------------------------------------------------------------------------
    Read the header from the XWD image in input stream 'ifP'.  Leave
    the stream positioned to the beginning of the raster.
 
    Return various fields from the header.
 
-   Return as *padrightP the number of additional pixels of padding are
+   Return as *padrightP the number of additional bits of padding are
    at the end of each line of input.  This says the input stream
-   contains *colsP pixels of image data plus *padrightP pixels of
-   padding.
+   contains *colsP pixels of image data (at *bits_per_pixelP bits each)
+   plus *padrightP bits of padding.
 -----------------------------------------------------------------------------*/
     /* Assume X11 headers are larger than X10 ones. */
     unsigned char header[sizeof(X11WDFileHeader)];
@@ -723,8 +749,7 @@ getinit(FILE *             const ifP,
         processX10Header(h10P, ifP, colsP, rowsP, padrightP, maxvalP, 
                          visualclassP, formatP, 
                          colorsP, bits_per_pixelP, bits_per_itemP, 
-                         red_maskP, green_maskP, blue_maskP, 
-                         byte_orderP, bit_orderP);
+                         compMaskP, byte_orderP, bit_orderP);
     } else if (h11P->file_version == X11WD_FILE_VERSION ||
                pm_bs_long(h11P->file_version) == X11WD_FILE_VERSION) {
         
@@ -742,11 +767,11 @@ getinit(FILE *             const ifP,
         if (headerDump)
             dumpX11Header(h11P);
 
-        processX11Header(h11P, ifP, colsP, rowsP, padrightP, maxvalP, 
+        processX11Header(h11P, ifP, cmapDump,
+                         colsP, rowsP, padrightP, maxvalP, 
                          visualclassP, formatP, 
                          colorsP, bits_per_pixelP, bits_per_itemP, 
-                         red_maskP, green_maskP, blue_maskP, 
-                         byte_orderP, bit_orderP);
+                         compMaskP, byte_orderP, bit_orderP);
     } else
         pm_error("unknown XWD file version: %u", h11P->file_version);
 }
@@ -799,13 +824,10 @@ getinit(FILE *             const ifP,
    one pixel at a time from it.
 
    It consists of a structure of type 'pixelReader' and the
-   getpix() and pixelReaderInit() subroutines.
+   pixelReader_*() subroutines.
 -----------------------------------------------------------------------------*/
 
 typedef struct {
-    /* This structure contains the state of the getpix() reader as it
-       reads across a row in the input image.
-       */
     FILE * fileP;
     unsigned long itemBuffer;
         /* The item buffer.  This contains what's left of the item
@@ -850,12 +872,12 @@ typedef struct {
 
 
 static void
-pixelReaderInit(pixelReader *  const pixelReaderP,
-                FILE *         const fileP,
-                int            const bitsPerPixel,
-                int            const bitsPerItem, 
-                enum byteorder const byteOrder,
-                enum byteorder const bitOrder) {
+pixelReader_init(pixelReader *  const pixelReaderP,
+                 FILE *         const fileP,
+                 int            const bitsPerPixel,
+                 int            const bitsPerItem, 
+                 enum byteorder const byteOrder,
+                 enum byteorder const bitOrder) {
     
     pixelReaderP->fileP           = fileP;
     pixelReaderP->bitsPerPixel    = bitsPerPixel;
@@ -869,6 +891,33 @@ pixelReaderInit(pixelReader *  const pixelReaderP,
 
 
 static void
+pixelReader_term(pixelReader * const pixelReaderP) {
+
+    unsigned int remainingByteCount;
+
+    if (pixelReaderP->nBitsLeft > 0)
+        pm_message("Warning: %u unused bits left in the pixel reader "
+                   "buffer after full image converted.  XWD file may be "
+                   "corrupted or Xwdtopnm may have misinterpreted it",
+                   pixelReaderP->nBitsLeft);
+
+
+    pm_drain(pixelReaderP->fileP, 4096, &remainingByteCount);
+
+    if (remainingByteCount >= 4096)
+        pm_message("Warning: at least 4K additional bytes in XWD input stream "
+                   "after full image converted.  XWD file may be corrupted "
+                   "or Xwdtopnm may have misinterpreted it.");
+    else if (remainingByteCount > 0)
+        pm_message("Warning: %u additional bytes in XWD input stream "
+                   "after full image converted.  XWD file may be corrupted "
+                   "or Xwdtopnm may have misinterpreted it.",
+                   remainingByteCount);
+}
+
+
+
+static void
 readItem(pixelReader * const rdrP) {
 /*----------------------------------------------------------------------------
    Read one item from the XWD raster associated with pixel reader *rdrP.
@@ -940,7 +989,6 @@ static unsigned long const lsbmask[] = {
 };
 
 
-
 static unsigned long
 pixelReader_getbits(pixelReader * const rdrP,
                     unsigned int  const nBits) {
@@ -1005,7 +1053,7 @@ pixelReader_getbits(pixelReader * const rdrP,
 
 
 static unsigned long
-getpix(pixelReader * const rdrP) {
+pixelReader_getpix(pixelReader * const rdrP) {
 /*----------------------------------------------------------------------------
    Get a pixel from the input image.
 
@@ -1056,15 +1104,13 @@ getpix(pixelReader * const rdrP) {
 static void
 reportInfo(int              const cols, 
            int              const rows, 
-           int              const padright, 
+           unsigned int     const padright, 
            xelval           const maxval, 
            enum visualclass const visualclass,
            int              const format, 
            int              const bits_per_pixel,
            int              const bits_per_item, 
-           int              const red_mask, 
-           int              const green_mask, 
-           int              const blue_mask,
+           struct compMask  const compMask,
            enum byteorder   const byte_order, 
            enum byteorder   const bit_order) {
     
@@ -1092,15 +1138,37 @@ reportInfo(int              const cols,
     }
     pm_message("%d rows of %d columns with maxval %d",
                rows, cols, maxval);
-    pm_message("padright=%d.  visualclass = %s.  format=%d (%c%c)",
+    pm_message("padright=%u bits.  visualclass = %s.  format=%d (%c%c)",
                padright, visualclass_name, 
                format, format/256, format%256);
     pm_message("bits_per_pixel=%d; bits_per_item=%d",
                bits_per_pixel, bits_per_item);
     pm_message("byte_order=%s; bit_order=%s",
                byte_order_name, bit_order_name);
-    pm_message("red_mask=0x%.8x; green_mask=0x%.8x; blue_mask=0x%.8x",
-               red_mask, green_mask, blue_mask);
+    pm_message("component mask: red=0x%.8lx; grn=0x%.8lx; blu=0x%.8lx",
+               compMask.red, compMask.grn, compMask.blu);
+}
+
+
+
+static void
+warn16Bit(xelval const maxval) {
+/*----------------------------------------------------------------------------
+   This program is often used by users of X, and those users often use
+   'xv', which doesn't properly interpret PNM files with 16 bit samples.
+   Furthermore, the maxval is often much larger than the user assumes
+   because of PNM's need to use the same maxval for all color components,
+   while XWD often uses different resolution for each.
+
+   Users get really frustrated when Xv displays something other than the
+   original mimage, almost always assuming that means Xwdtopnm converted
+   incorrectly.
+-----------------------------------------------------------------------------*/
+
+    if (pm_maxvaltobits(maxval) > 8)
+        pm_message("WARNING: Producing maxval %u output.  This involves "
+                   "multiple bytes per sample, which some programs, e.g. "
+                   "'xv', can't handle.  See manual.", maxval);
 }
 
 
@@ -1113,36 +1181,34 @@ convertRowSimpleIndex(pixelReader *  const pixelReaderP,
     
     unsigned int col;
     for (col = 0; col < cols; ++col)
-        xelrow[col] = colors[getpix(pixelReaderP)];
+        xelrow[col] = colors[pixelReader_getpix(pixelReaderP)];
 }
 
 
 
 static void
-convertRowDirect(pixelReader *  const pixelReaderP,
-                 int            const cols,
-                 const xel *    const colors,
-                 unsigned long  const red_mask,
-                 unsigned long  const grn_mask,
-                 unsigned long  const blu_mask,
-                 xel *          const xelrow) {
+convertRowDirect(pixelReader *   const pixelReaderP,
+                 int             const cols,
+                 const xel *     const colors,
+                 struct compMask const compMask,
+                 xel *           const xelrow) {
         
     unsigned int col;
 
     for (col = 0; col < cols; ++col) {
         unsigned long pixel;
             /* This is a triplet of indices into the color map, packed
-               into this bit string according to red_mask, etc.
+               into this bit string according to compMask
             */
         unsigned int red_index, grn_index, blu_index;
             /* These are indices into the color map, unpacked from 'pixel'.
              */
             
-        pixel = getpix(pixelReaderP);
+        pixel = pixelReader_getpix(pixelReaderP);
 
-        red_index = (pixel & red_mask) >> zero_bits(red_mask);
-        grn_index = (pixel & grn_mask) >> zero_bits(grn_mask); 
-        blu_index = (pixel & blu_mask) >> zero_bits(blu_mask);
+        red_index = (pixel & compMask.red) >> zero_bits(compMask.red);
+        grn_index = (pixel & compMask.grn) >> zero_bits(compMask.grn); 
+        blu_index = (pixel & compMask.blu) >> zero_bits(compMask.blu);
 
         PPM_ASSIGN(xelrow[col],
                    PPM_GETR(colors[red_index]),
@@ -1155,39 +1221,37 @@ convertRowDirect(pixelReader *  const pixelReaderP,
 
 
 static void
-convertRowTrueColor(pixelReader *  const pixelReaderP,
-                    int                  const cols,
-                    pixval               const maxval,
-                    const xel *          const colors,
-                    unsigned long        const red_mask,
-                    unsigned long        const grn_mask,
-                    unsigned long        const blu_mask,
-                    xel *                const xelrow) {
+convertRowTrueColor(pixelReader *   const pixelReaderP,
+                    int             const cols,
+                    pixval          const maxval,
+                    const xel *     const colors,
+                    struct compMask const compMask,
+                    xel *           const xelrow) {
 
     unsigned int col;
     unsigned int red_shift, grn_shift, blu_shift;
     unsigned int red_maxval, grn_maxval, blu_maxval;
 
-    red_shift = zero_bits(red_mask);
-    grn_shift = zero_bits(grn_mask);
-    blu_shift = zero_bits(blu_mask);
+    red_shift = zero_bits(compMask.red);
+    grn_shift = zero_bits(compMask.grn);
+    blu_shift = zero_bits(compMask.blu);
 
-    red_maxval = red_mask >> red_shift;
-    grn_maxval = grn_mask >> grn_shift;
-    blu_maxval = blu_mask >> blu_shift;
+    red_maxval = compMask.red >> red_shift;
+    grn_maxval = compMask.grn >> grn_shift;
+    blu_maxval = compMask.blu >> blu_shift;
 
     for (col = 0; col < cols; ++col) {
         unsigned long pixel;
 
-        pixel = getpix(pixelReaderP);
+        pixel = pixelReader_getpix(pixelReaderP);
 
         /* The parsing of 'pixel' used to be done with hardcoded layout
            parameters.  See comments at end of this file.
         */
         PPM_ASSIGN(xelrow[col],
-                   ((pixel & red_mask) >> red_shift) * maxval / red_maxval,
-                   ((pixel & grn_mask) >> grn_shift) * maxval / grn_maxval,
-                   ((pixel & blu_mask) >> blu_shift) * maxval / blu_maxval
+                   ((pixel & compMask.red) >> red_shift) * maxval / red_maxval,
+                   ((pixel & compMask.grn) >> grn_shift) * maxval / grn_maxval,
+                   ((pixel & compMask.blu) >> blu_shift) * maxval / blu_maxval
             );
 
     }
@@ -1198,13 +1262,11 @@ convertRowTrueColor(pixelReader *  const pixelReaderP,
 static void
 convertRow(pixelReader *    const pixelReaderP,
            FILE *           const ofP,
-           int              const padright, 
+           unsigned int     const padright, 
            int              const cols, 
            xelval           const maxval,
            int              const format, 
-           unsigned long    const red_mask, 
-           unsigned long    const green_mask, 
-           unsigned long    const blue_mask, 
+           struct compMask  const compMask,
            const xel*       const colors, 
            enum visualclass const visualclass) {
 /*----------------------------------------------------------------------------
@@ -1214,7 +1276,7 @@ convertRow(pixelReader *    const pixelReaderP,
    The row is 'cols' pixels.
 
    After reading the 'cols' pixels, we read and discard an additional
-   'padright' pixels from the input stream, so as to read the entire
+   'padright' bits from the input stream, so as to read the entire
    input line.
 -----------------------------------------------------------------------------*/
     xel* xelrow;
@@ -1228,22 +1290,19 @@ convertRow(pixelReader *    const pixelReaderP,
         convertRowSimpleIndex(pixelReaderP, cols, colors, xelrow);
         break;
     case DirectColor: 
-        convertRowDirect(pixelReaderP, cols, colors,
-                         red_mask, green_mask, blue_mask,
-                         xelrow);
+        convertRowDirect(pixelReaderP, cols, colors, compMask, xelrow);
         
         break;
     case TrueColor: 
         convertRowTrueColor(pixelReaderP, cols, maxval, colors,
-                            red_mask, green_mask, blue_mask,
-                            xelrow);
+                            compMask, xelrow);
         break;
             
     default:
         pm_error("unknown visual class");
     }
     pixelReader_getbits(pixelReaderP, padright);
-
+    
     pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
     pnm_freerow(xelrow);
 }
@@ -1278,15 +1337,17 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    int rows, cols, format, padright;
+    int rows, cols, format;
+    unsigned int padright;
+        /* Number of bits of padding on the right of each row */
     unsigned int row;
-    int bits_per_pixel;
-    int bits_per_item;
-    unsigned long red_mask, green_mask, blue_mask;
+    int bitsPerPixel;
+    int bitsPerItem;
+    struct compMask compMask;
     xelval maxval;
     enum visualclass visualclass;
-    enum byteorder byte_order, bit_order;
-    xel *colors;  /* the color map */
+    enum byteorder byteOrder, bitOrder;
+    xel * colors;  /* the color map */
     pixelReader pixelReader;
 
     pnm_init(&argc, argv);
@@ -1296,24 +1357,25 @@ main(int argc, char *argv[]) {
     debug = cmdline.debug;
     verbose = cmdline.verbose;
 
-    if (cmdline.input_filename != NULL) 
-        ifP = pm_openr(cmdline.input_filename);
+    if (cmdline.inputFilename != NULL) 
+        ifP = pm_openr(cmdline.inputFilename);
     else
         ifP = stdin;
 
     getinit(ifP, &cols, &rows, &padright, &maxval, &visualclass, &format, 
-            &colors, &bits_per_pixel, &bits_per_item, 
-            &red_mask, &green_mask, &blue_mask, &byte_order, &bit_order,
-            cmdline.headerdump);
+            &colors, &bitsPerPixel, &bitsPerItem, 
+            &compMask, &byteOrder, &bitOrder,
+            cmdline.headerdump, cmdline.cmapdump);
+
+    warn16Bit(maxval);
     
     if (verbose) 
         reportInfo(cols, rows, padright, maxval, visualclass,
-                   format, bits_per_pixel, bits_per_item,
-                   red_mask, green_mask, blue_mask, 
-                   byte_order, bit_order);
+                   format, bitsPerPixel, bitsPerItem, compMask,
+                   byteOrder, bitOrder);
 
-    pixelReaderInit(&pixelReader, ifP, bits_per_pixel, bits_per_item,
-                    byte_order, bit_order);
+    pixelReader_init(&pixelReader, ifP, bitsPerPixel, bitsPerItem,
+                     byteOrder, bitOrder);
 
     pnm_writepnminit(stdout, cols, rows, maxval, format, 0);
 
@@ -1321,9 +1383,11 @@ main(int argc, char *argv[]) {
 
     for (row = 0; row < rows; ++row) {
         convertRow(&pixelReader, stdout,
-                   padright, cols, maxval, format,
-                   red_mask, green_mask, blue_mask, colors, visualclass);
+                   padright, cols, maxval, format, compMask,
+                   colors, visualclass);
     }
+
+    pixelReader_term(&pixelReader);
     
     pm_close(ifP);
     pm_close(stdout);
@@ -1332,6 +1396,7 @@ main(int argc, char *argv[]) {
 }
 
 
+
 /*
    This used to be the way we parsed a direct/true color pixel.  I'm 
    keeping it here in case we find out some application needs it this way.